From 780a38497567717bc0408e2f29fc4eea44864dce Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 4 Jan 2024 17:37:04 +0700 Subject: [PATCH 001/133] feat: coinjoin integration draft --- .../Models/CoinJoin/DSTransaction+CoinJoin.h | 31 +++ .../Models/CoinJoin/DSTransaction+CoinJoin.m | 84 +++++++ .../CoinJoin/DSTransactionInput+CoinJoin.h | 31 +++ .../CoinJoin/DSTransactionInput+CoinJoin.m | 59 +++++ .../CoinJoin/DSTransactionOutput+CoinJoin.h | 31 +++ .../CoinJoin/DSTransactionOutput+CoinJoin.m | 58 +++++ .../Managers/Chain Managers/DSPeerManager.m | 5 +- DashSync/shared/Models/Wallet/DSAccount.h | 3 + DashSync/shared/Models/Wallet/DSAccount.m | 16 ++ DashSync/shared/Models/Wallet/DSWallet.h | 3 + DashSync/shared/Models/Wallet/DSWallet.m | 8 + Example/DashSync.xcodeproj/project.pbxproj | 92 ++++++++ Example/DashSync/Actions.storyboard | 215 +++++++++++++----- Example/DashSync/Base.lproj/Main.storyboard | 180 +++++++-------- Example/DashSync/DSActionsViewController.m | 4 + Example/DashSync/DSCoinJoinViewController.h | 28 +++ Example/DashSync/DSCoinJoinViewController.m | 131 +++++++++++ 17 files changed, 825 insertions(+), 154 deletions(-) create mode 100644 DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.h create mode 100644 DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m create mode 100644 DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.h create mode 100644 DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.m create mode 100644 DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.h create mode 100644 DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m create mode 100644 Example/DashSync/DSCoinJoinViewController.h create mode 100644 Example/DashSync/DSCoinJoinViewController.m diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.h b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.h new file mode 100644 index 000000000..7ccf84485 --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.h @@ -0,0 +1,31 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSTransaction.h" +#import "dash_shared_core.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSTransaction (CoinJoin) + +- (Transaction *)ffi_malloc:(ChainType)chainType; ++ (void)ffi_free:(Transaction *)tx; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m new file mode 100644 index 000000000..9600c8ea2 --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -0,0 +1,84 @@ +// +// Created by Andrei Ashikhmin +// 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 "BigIntTypes.h" +#import "DSTransaction.h" +#import "DSTransaction+CoinJoin.h" +#import "DSTransactionInput+CoinJoin.h" +#import "DSTransactionOutput+CoinJoin.h" +#import "NSData+Dash.h" + +@implementation DSTransaction (CoinJoin) + +- (Transaction *)ffi_malloc:(ChainType)chainType { + Transaction *transaction = malloc(sizeof(Transaction)); + + transaction->tx_hash = uint256_malloc(self.txHash); + uintptr_t inputsCount = self.inputs.count; + uintptr_t outputsCount = self.outputs.count; + transaction->inputs_count = inputsCount; + transaction->outputs_count = outputsCount; + + TransactionInput **inputsArray = malloc(inputsCount * sizeof(TransactionInput *)); + TransactionOutput **outputsArray = malloc(outputsCount * sizeof(TransactionOutput *)); + + for (uintptr_t i = 0; i < inputsCount; ++i) { + inputsArray[i] = [self.inputs[i] ffi_malloc]; + } + + for (uintptr_t i = 0; i < outputsCount; ++i) { + outputsArray[i] = [self.outputs[i] ffi_malloc:chainType]; + } + + transaction->inputs = inputsArray; + transaction->outputs = outputsArray; + transaction->lock_time = self.lockTime; + transaction->version = self.version; + transaction->tx_type = (TransactionType)self.type; + transaction->payload_offset = self.payloadOffset; + transaction->block_height = self.blockHeight; + + return transaction; +} + ++ (void)ffi_free:(Transaction *)tx { + if (!tx) return; + + free(tx->tx_hash); + + if (tx->inputs_count > 0 && tx->inputs) { + for (int i = 0; i < tx->inputs_count; i++) { + [DSTransactionInput ffi_free:tx->inputs[i]]; + } + + free(tx->inputs); + } + + if (tx->outputs_count > 0 && tx->outputs) { + for (int i = 0; i < tx->outputs_count; i++) { + [DSTransactionOutput ffi_free:tx->outputs[i]]; + } + + free(tx->outputs); + } + + free(tx); + DSLog(@"[OBJ-C] CoinJoin: 💀 transaction"); +} + +@end + diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.h b/DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.h new file mode 100644 index 000000000..342a36709 --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.h @@ -0,0 +1,31 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSTransactionInput.h" +#import "dash_shared_core.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DSTransactionInput (CoinJoin) + +- (TransactionInput *)ffi_malloc; ++ (void)ffi_free:(TransactionInput *)input; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.m new file mode 100644 index 000000000..d7b822f6e --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSTransactionInput+CoinJoin.m @@ -0,0 +1,59 @@ +// +// Created by Andrei Ashikhmin +// 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 "BigIntTypes.h" +#import "DSTransactionInput.h" +#import "DSTransactionInput+CoinJoin.h" +#import "NSData+Dash.h" + +@implementation DSTransactionInput (CoinJoin) + +- (TransactionInput *)ffi_malloc { + TransactionInput *transactionInput = malloc(sizeof(TransactionInput)); + transactionInput->input_hash = uint256_malloc(self.inputHash); + transactionInput->index = self.index; + transactionInput->sequence = self.sequence; + + NSData *scriptData = self.inScript; + transactionInput->script_length = scriptData.length; + transactionInput->script = data_malloc(scriptData); + + NSData *signatureData = self.signature; + transactionInput->signature_length = signatureData.length; + transactionInput->signature = data_malloc(signatureData); + + return transactionInput; +} + ++ (void)ffi_free:(TransactionInput *)input { + if (!input) return; + + free(input->input_hash); + + if (input->script) { + free(input->script); + } + + if (input->signature) { + free(input->signature); + } + + free(input); +} + +@end + diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.h b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.h new file mode 100644 index 000000000..39f706b67 --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.h @@ -0,0 +1,31 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSTransactionOutput.h" +#import "dash_shared_core.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DSTransactionOutput (CoinJoin) + +- (TransactionOutput *)ffi_malloc:(ChainType) type; ++ (void)ffi_free:(TransactionOutput *)output; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m new file mode 100644 index 000000000..2caadec3f --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m @@ -0,0 +1,58 @@ +// +// Created by Andrei Ashikhmin +// 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 "BigIntTypes.h" +#import "DSTransactionOutput.h" +#import "DSTransactionOutput+CoinJoin.h" +#import "NSData+Dash.h" + +@implementation DSTransactionOutput (CoinJoin) + +- (TransactionOutput *)ffi_malloc:(ChainType)type { + TransactionOutput *transactionOutput = malloc(sizeof(TransactionOutput)); + transactionOutput->amount = self.amount; + + NSUInteger length = self.outScript.length; + transactionOutput->script_length = (uintptr_t)length; + NSData *scriptData = self.outScript; + transactionOutput->script = data_malloc(scriptData); + + char *c_string = address_with_script_pubkey(self.outScript.bytes, self.outScript.length, type); + size_t addressLength = strlen(c_string); + transactionOutput->address_length = (uintptr_t)addressLength; + transactionOutput->address = (uint8_t *)c_string; + + return transactionOutput; +} + ++ (void)ffi_free:(TransactionOutput *)output { + if (!output) return; + + if (output->script) { + free(output->script); + } + + if (output->address) { + // TODO: should we use processor_destroy_string(c_string) here? + free(output->address); + } + + free(output); +} + +@end + diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index 1a8a53a8c..7a1ef376f 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -700,7 +700,10 @@ - (void)connect { DSLog(@"[%@] [DSPeerManager] connect", self.chain.name); self.desiredState = DSPeerManagerDesiredState_Connected; dispatch_async(self.networkingQueue, ^{ - if ([self.chain syncsBlockchain] && ![self.chain canConstructAFilter]) return; // check to make sure the wallet has been created if only are a basic wallet with no dash features + if ([self.chain syncsBlockchain] && ![self.chain canConstructAFilter]) { + DSLog(@"[%@] [DSPeerManager] failed to connect: check that wallet is created", self.chain.name); + return; // check to make sure the wallet has been created if only are a basic wallet with no dash features + } if (self.connectFailures >= MAX_CONNECT_FAILURES) self.connectFailures = 0; // this attempt is a manual retry @synchronized (self.chainManager) { if (self.chainManager.terminalHeaderSyncProgress < 1.0) { diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index 3db72ad65..8808733ae 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -215,6 +215,9 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // true if no previous account transaction spends any of the given transaction's inputs, and no inputs are invalid - (BOOL)transactionIsValid:(DSTransaction *)transaction; +// returns input value if no previous account transaction spends this input, and the input is valid, -1 otherwise. +- (int64_t)inputValue:(UInt256)txHash inputIndex:(uint32_t)index; + // received, sent or moved inside an account - (DSTransactionDirection)directionOfTransaction:(DSTransaction *)transaction; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index b0a9fe375..1ced23d20 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1502,6 +1502,22 @@ - (BOOL)transactionIsValid:(DSTransaction *)transaction { } } +- (int64_t)inputValue:(UInt256)txHash inputIndex:(uint32_t)index { + NSValue *hash = uint256_obj(txHash); + DSTransaction *tx = self.allTx[hash]; + + if (tx == NULL) { + return -1; + } + + if (![self transactionIsValid:tx] || + [self.spentOutputs containsObject:dsutxo_obj(((DSUTXO){txHash, index}))]) { + return -1; + } + + return (int64_t)tx.outputs[index].amount; +} + // true if transaction cannot be immediately spent (i.e. if it or an input tx can be replaced-by-fee) - (BOOL)transactionIsPending:(DSTransaction *)transaction { NSParameterAssert(transaction); diff --git a/DashSync/shared/Models/Wallet/DSWallet.h b/DashSync/shared/Models/Wallet/DSWallet.h index f6a3d6993..229350574 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.h +++ b/DashSync/shared/Models/Wallet/DSWallet.h @@ -189,6 +189,9 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; // true if no previous wallet transaction spends any of the given transaction's inputs, and no inputs are invalid - (BOOL)transactionIsValid:(DSTransaction *)transaction; +// returns input value if no previous wallet transaction spends this input, and the input is valid, -1 otherwise. +- (int64_t)inputValue:(UInt256)txHash inputIndex:(uint32_t)index; + // 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/DSWallet.m b/DashSync/shared/Models/Wallet/DSWallet.m index 9eee22251..974ba7587 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.m +++ b/DashSync/shared/Models/Wallet/DSWallet.m @@ -1084,6 +1084,14 @@ - (BOOL)transactionIsValid:(DSTransaction *_Nonnull)transaction { return TRUE; } +- (int64_t)inputValue:(UInt256)txHash inputIndex:(uint32_t)index { + for (DSAccount *account in self.accounts) { + int64_t value = [account inputValue:txHash inputIndex:index]; + if (value != -1) return value; + } + return -1; +} + - (OpaqueKey *)privateKeyForAddress:(NSString *)address fromSeed:(NSData *)seed { NSParameterAssert(address); NSParameterAssert(seed); diff --git a/Example/DashSync.xcodeproj/project.pbxproj b/Example/DashSync.xcodeproj/project.pbxproj index 30361eaac..d045153bb 100644 --- a/Example/DashSync.xcodeproj/project.pbxproj +++ b/Example/DashSync.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; + 758719952B3ECB7A004C0E2D /* DSCoinJoinViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 758719942B3ECB7A004C0E2D /* DSCoinJoinViewController.m */; }; 798A60FA627EC43C0AB39611 /* libPods-DashSync-DashSync_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 192956F40996DE819B9D64AC /* libPods-DashSync-DashSync_Example.a */; }; 83CFCA70892A5DF9F64BA1AA /* libPods-DashSync_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 955D615511918752AF099181 /* libPods-DashSync_Tests.a */; }; 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; @@ -616,6 +617,8 @@ 6675EA01A2AC0589160069A4 /* libPods-NetworkInfo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NetworkInfo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 74FDA0D6BCD2D779E20B58FC /* Pods-DashSync-DashSync_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DashSync-DashSync_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-DashSync-DashSync_Example/Pods-DashSync-DashSync_Example.release.xcconfig"; sourceTree = ""; }; + 758719932B3ECB28004C0E2D /* DSCoinJoinViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCoinJoinViewController.h; sourceTree = ""; }; + 758719942B3ECB7A004C0E2D /* DSCoinJoinViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCoinJoinViewController.m; sourceTree = ""; }; 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 = ""; }; @@ -2032,6 +2035,8 @@ FB83C2E621B91A8D0057A0EF /* DSTransactionFloodingViewController.m */, FB9762F524BC4C3E00CF28EC /* DSMiningViewController.h */, FB9762F624BC4C3E00CF28EC /* DSMiningViewController.m */, + 758719932B3ECB28004C0E2D /* DSCoinJoinViewController.h */, + 758719942B3ECB7A004C0E2D /* DSCoinJoinViewController.m */, ); name = Actions; sourceTree = ""; @@ -2984,6 +2989,7 @@ FBBDF42C226EDF600081D673 /* DSContactTableViewCell.m in Sources */, FB83C2E721B91A8D0057A0EF /* DSTransactionFloodingViewController.m in Sources */, FB12FEEA20CD50AC00C1E0F0 /* DSStandaloneDerivationPathViewController.m in Sources */, + 758719952B3ECB7A004C0E2D /* DSCoinJoinViewController.m in Sources */, FB9762F724BC4C3E00CF28EC /* DSMiningViewController.m in Sources */, FB850013261A7B9A0002E5EA /* DSIdentityChooserViewController.m in Sources */, FB90D2CC20C3F0D3002B7956 /* DSAccountTableViewCell.m in Sources */, @@ -3144,6 +3150,7 @@ "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_FAST_MATH = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -3199,6 +3206,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_FAST_MATH = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -3225,6 +3233,48 @@ INFOPLIST_FILE = "DashSync/DashSync-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MODULE_NAME = ExampleApp; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-l\"BoringSSL-GRPC-iOS\"", + "-l\"CocoaImageHashing-iOS\"", + "-l\"CocoaLumberjack-iOS\"", + "-l\"DAPI-GRPC-iOS\"", + "-l\"DSDynamicOptions-iOS\"", + "-l\"DWAlertController\"", + "-l\"DashSync-iOS\"", + "-l\"KVO-MVVM\"", + "-l\"Protobuf-iOS\"", + "-l\"SDWebImage-iOS\"", + "-l\"TinyCborObjc-iOS\"", + "-l\"abseil-iOS\"", + "-l\"bz2\"", + "-l\"c++\"", + "-l\"dash_shared_core_ios\"", + "-l\"gRPC-Core-iOS\"", + "-l\"gRPC-ProtoRPC-iOS\"", + "-l\"gRPC-RxLibrary-iOS\"", + "-l\"gRPC-iOS\"", + "-l\"resolv\"", + "-l\"sqlite3\"", + "-l\"tinycbor-iOS\"", + "-l\"z\"", + "-framework", + "\"BackgroundTasks\"", + "-framework", + "\"CoreData\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"ImageIO\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-framework", + "\"UIKit\"", + "-ld64", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; @@ -3243,6 +3293,48 @@ INFOPLIST_FILE = "DashSync/DashSync-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MODULE_NAME = ExampleApp; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-l\"BoringSSL-GRPC-iOS\"", + "-l\"CocoaImageHashing-iOS\"", + "-l\"CocoaLumberjack-iOS\"", + "-l\"DAPI-GRPC-iOS\"", + "-l\"DSDynamicOptions-iOS\"", + "-l\"DWAlertController\"", + "-l\"DashSync-iOS\"", + "-l\"KVO-MVVM\"", + "-l\"Protobuf-iOS\"", + "-l\"SDWebImage-iOS\"", + "-l\"TinyCborObjc-iOS\"", + "-l\"abseil-iOS\"", + "-l\"bz2\"", + "-l\"c++\"", + "-l\"dash_shared_core_ios\"", + "-l\"gRPC-Core-iOS\"", + "-l\"gRPC-ProtoRPC-iOS\"", + "-l\"gRPC-RxLibrary-iOS\"", + "-l\"gRPC-iOS\"", + "-l\"resolv\"", + "-l\"sqlite3\"", + "-l\"tinycbor-iOS\"", + "-l\"z\"", + "-framework", + "\"BackgroundTasks\"", + "-framework", + "\"CoreData\"", + "-framework", + "\"Foundation\"", + "-framework", + "\"ImageIO\"", + "-framework", + "\"Security\"", + "-framework", + "\"SystemConfiguration\"", + "-framework", + "\"UIKit\"", + "-ld64", + ); PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; diff --git a/Example/DashSync/Actions.storyboard b/Example/DashSync/Actions.storyboard index f289fa71a..e3e8991a7 100644 --- a/Example/DashSync/Actions.storyboard +++ b/Example/DashSync/Actions.storyboard @@ -1,10 +1,11 @@ - + - + + @@ -20,14 +21,14 @@ - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + @@ -117,7 +141,7 @@ - + @@ -131,14 +155,14 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/DashSync/Base.lproj/Main.storyboard b/Example/DashSync/Base.lproj/Main.storyboard index d07e791d3..9b8c710de 100644 --- a/Example/DashSync/Base.lproj/Main.storyboard +++ b/Example/DashSync/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -81,7 +81,7 @@ - + @@ -121,10 +121,10 @@ - + - + - + - + - + - + - + - + - + - + From 099912703eb2217a02090af44108a1b42a33f827 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 14 Mar 2024 19:25:27 +0700 Subject: [PATCH 011/133] fix: wait for denom tx to be published before making collateral --- .../shared/Models/CoinJoin/DSCoinJoinWrapper.h | 3 ++- .../shared/Models/CoinJoin/DSCoinJoinWrapper.m | 4 +++- Example/DashSync/DSCoinJoinViewController.m | 15 ++++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 0a8ab8938..69bc2af55 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -29,6 +29,7 @@ NS_ASSUME_NONNULL_BEGIN @property (strong, nonatomic, readonly) DSChain *chain; @property (nonatomic, assign, nullable) CoinJoinClientSession *clientSession; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; +@property (nonatomic, assign) uint64_t balance_needs_anonymized; @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; @@ -40,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount; - (ByteArray)freshAddress:(BOOL)internal; -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs; +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index e8b995c98..515ee9d41 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -359,7 +359,7 @@ - (ByteArray)freshAddress:(BOOL)internal { return script_pubkey_for_address([address UTF8String], self.chain.chainType); } -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs { +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished { DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES]; @@ -376,8 +376,10 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { if (error) { DSLog(@"[OBJ-C] CoinJoin publish error: %@", error.description); + onPublished(error); } else { DSLog(@"[OBJ-C] CoinJoin publish success: %@", transaction.description); + onPublished(nil); } }]; } diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index 4e8ef5e2f..cc56c14b5 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -97,8 +97,8 @@ -(void)runCoinJoin { balance->watch_only_untrusted_pending = 0; balance->watch_only_immature = 0; DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.wrapper.chain.balance); - BOOL result = call_session(_clientSession, *balance); - DSLog(@"[OBJ-C] CoinJoin: call result: %s", result ? "TRUE" : "FALSE"); + self.wrapper.balance_needs_anonymized = do_automatic_denominating(_clientSession, *balance); + DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); free(balance); } @@ -317,7 +317,16 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, const voi bool result = false; @synchronized (context) { - result = [AS_OBJC(context) commitTransactionForAmounts:amounts outputs:scripts]; + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + result = [wrapper commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { + if (!error) { + // TODO: let balance_denominated_unconf = balance_info.denominated_untrusted_pending; + uint64_t balanceDenominatedUnconf = 0; + DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); + bool isFinished = finish_automatic_denominating(wrapper.clientSession, balanceDenominatedUnconf, wrapper.balance_needs_anonymized); + DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); + } + }]; } return result; From 4c11654e20102bc20c46cd39516f88338039092a Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 28 Mar 2024 19:14:01 +0700 Subject: [PATCH 012/133] feat: CoinJoinClientManager callbacks --- .../Models/CoinJoin/DSCoinJoinWrapper.h | 13 ++-- .../Models/CoinJoin/DSCoinJoinWrapper.m | 28 ++++++-- Example/DashSync/DSCoinJoinViewController.m | 67 ++++++++++++++++--- 3 files changed, 90 insertions(+), 18 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 69bc2af55..5eea9c91b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -1,4 +1,4 @@ -// +// // Created by Andrei Ashikhmin // Copyright © 2024 Dash Core Group. All rights reserved. // @@ -26,22 +26,25 @@ NS_ASSUME_NONNULL_BEGIN @interface DSCoinJoinWrapper : NSObject // TODO: free on dealloc -@property (strong, nonatomic, readonly) DSChain *chain; +@property (strong, nonatomic, readonly) DSChainManager *chainManager; @property (nonatomic, assign, nullable) CoinJoinClientSession *clientSession; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; @property (nonatomic, assign) uint64_t balance_needs_anonymized; - @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; +@property (nonatomic, strong) DSChain *chain; -- (instancetype)initWithChain:(DSChain *)chain; +- (instancetype)initWithChainManager:(DSChainManager *)chainManager; - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; - (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; - (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount; -- (ByteArray)freshAddress:(BOOL)internal; +- (NSString *)freshAddress:(BOOL)internal; - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished; +- (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; +- (uint64_t)validMNCount; +- (DSMasternodeList *)mnList; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 515ee9d41..f512de804 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -1,4 +1,4 @@ -// +// // Created by Andrei Ashikhmin // Copyright © 2024 Dash Core Group. All rights reserved. // @@ -25,20 +25,25 @@ #import "NSString+Bitcoin.h" #import "DSChainManager.h" #import "DSTransactionManager.h" +#import "DSMasternodeManager.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @implementation DSCoinJoinWrapper -- (instancetype)initWithChain:(DSChain *)chain { +- (instancetype)initWithChainManager:(DSChainManager *)chainManager { self = [super init]; if (self) { - _chain = chain; + _chainManager = chainManager; } return self; } +- (DSChain *)chain { + return self.chainManager.chain; +} + - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { DSTransaction *tx = [self.chain transactionForHash:txHash]; DSAccount *account = [self.chain firstAccountThatCanContainTransaction:tx]; @@ -344,7 +349,7 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { return ret; } -- (ByteArray)freshAddress:(BOOL)internal { +- (NSString *)freshAddress:(BOOL)internal { NSString *address; DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; @@ -356,7 +361,7 @@ - (ByteArray)freshAddress:(BOOL)internal { DSLog(@"[OBJ-C] CoinJoin: freshReceiveAddress, address: %@", address); } - return script_pubkey_for_address([address UTF8String], self.chain.chainType); + return address; } - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished { @@ -387,4 +392,17 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output return YES; } +- (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash { + return [self.chainManager.masternodeManager.currentMasternodeList masternodeForRegistrationHash:hash]; +} + +- (uint64_t)validMNCount { + return self.chainManager.masternodeManager.currentMasternodeList.validMasternodeCount; +} + +- (DSMasternodeList *)mnList { + return self.chainManager.masternodeManager.currentMasternodeList; +} + @end + diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index cc56c14b5..97c9cef52 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -1,4 +1,4 @@ -// +// // Created by Andrei Ashikhmin // Copyright © 2023 Dash Core Group. All rights reserved. // @@ -22,6 +22,8 @@ #import "DSTransactionOutput+CoinJoin.h" #import "DSCoinControl.h" #import "DSCoinJoinWrapper.h" +#import "DSSimplifiedMasternodeEntry+Mndiff.h" +#import "DSMasternodeList+Mndiff.h" #define AS_OBJC(context) ((__bridge DSCoinJoinWrapper *)(context)) #define AS_RUST(context) ((__bridge void *)(context)) @@ -53,10 +55,9 @@ - (void)startCoinJoin { // TODO: refreshUnusedKeys() if (_wrapper == NULL) { - DSChain *chain = self.chainManager.chain; - _wrapper = [[DSCoinJoinWrapper alloc] initWithChain:chain]; - } - + _wrapper = [[DSCoinJoinWrapper alloc] initWithChainManager:self.chainManager]; + } + [self runCoinJoin]; } @@ -81,7 +82,7 @@ -(void)runCoinJoin { } if (_clientSession == NULL) { - _clientSession = register_client_session(_coinJoin, _options, getTransaction, destroyTransaction, isMineInput, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, signTransaction, countInputsWithAmount, freshCoinJoinAddress, commitTransaction, AS_RUST(self.wrapper)); + _clientSession = register_client_session(_coinJoin, _options, getTransaction, destroyTransaction, isMineInput, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, signTransaction, countInputsWithAmount, freshCoinJoinAddress, commitTransaction, masternodeByHash, destroyMasternodeEntry, AS_RUST(self.wrapper)); self.wrapper.clientSession = _clientSession; } @@ -295,9 +296,10 @@ unsigned int countInputsWithAmount(unsigned long long inputAmount, const void *c ByteArray freshCoinJoinAddress(bool internal, const void *context) { DSLog(@"[OBJ-C CALLBACK] CoinJoin: freshCoinJoinAddress"); - ByteArray array = [AS_OBJC(context) freshAddress:internal]; + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSString *address = [wrapper freshAddress:internal]; - return array; + return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); } bool commitTransaction(struct Recipient **items, uintptr_t item_count, const void *context) { @@ -332,4 +334,53 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, const voi return result; } +MasternodeEntry* masternodeByHash(uint8_t (*hash)[32], const void *context) { + UInt256 mnHash = *((UInt256 *)hash); + MasternodeEntry *masternode; + + @synchronized (context) { + masternode = [[AS_OBJC(context) masternodeEntryByHash:mnHash] ffi_malloc]; + } + + return masternode; +} + +void destroyMasternodeEntry(MasternodeEntry *masternodeEntry) { + if (!masternodeEntry) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeEntry"); + [DSSimplifiedMasternodeEntry ffi_free:masternodeEntry]; +} + +uint64_t validMNCount(const void *context) { + uint64_t result = 0; + + @synchronized (context) { + result = [AS_OBJC(context) validMNCount]; + } + + return result; +} + +MasternodeList* getMNList(const void *context) { + MasternodeList *masternodes; + + @synchronized (context) { + masternodes = [[AS_OBJC(context) mnList] ffi_malloc]; + } + + return masternodes; +} + +void destroyMNList(MasternodeList *masternodeList) { + if (!masternodeList) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeList"); + [DSMasternodeList ffi_free:masternodeList]; +} + @end From 8fe3babf3d011eb8c88255612f29e34e4e620cb7 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 8 Apr 2024 16:27:52 +0700 Subject: [PATCH 013/133] chore: refactor code into CoinJoinManager --- .../Models/CoinJoin/DSCoinJoinManager.h | 41 ++ .../Models/CoinJoin/DSCoinJoinManager.m | 403 ++++++++++++++++++ .../Models/CoinJoin/DSCoinJoinWrapper.h | 11 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 8 +- .../CoinJoin/{ => Model}/DSCoinControl.h | 0 .../CoinJoin/{ => Model}/DSCoinControl.m | 0 .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 28 ++ .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 27 ++ Example/DashSync/DSCoinJoinViewController.h | 9 +- Example/DashSync/DSCoinJoinViewController.m | 339 +-------------- 10 files changed, 519 insertions(+), 347 deletions(-) create mode 100644 DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h create mode 100644 DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m rename DashSync/shared/Models/CoinJoin/{ => Model}/DSCoinControl.h (100%) rename DashSync/shared/Models/CoinJoin/{ => Model}/DSCoinControl.m (100%) create mode 100644 DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h create mode 100644 DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h new file mode 100644 index 000000000..3d01acbcd --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -0,0 +1,41 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSMasternodeGroup.h" +#import "DSChainManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@class DSCoinJoinWrapper; + +@interface DSCoinJoinManager : NSObject + +@property (nonatomic, strong, nullable) DSChainManager *chainManager; +@property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; +@property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; + +@property (nonatomic, assign, nullable) WalletEx *walletEx; +@property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; +@property (nonatomic, assign, nullable) CoinJoinClientOptions *options; + +- (instancetype)initWithChainManager:(DSChainManager *)chainManager; +- (void)runCoinJoin; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m new file mode 100644 index 000000000..531b87ae7 --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -0,0 +1,403 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSCoinJoinManager.h" +#import "DSWallet.h" +#import "DSTransaction+CoinJoin.h" +#import "DSTransactionOutput+CoinJoin.h" +#import "DSSimplifiedMasternodeEntry+Mndiff.h" +#import "DSMasternodeList+Mndiff.h" +#import "DSAccount.h" +#import "DSChainManager.h" +#import "DSCoinJoinWrapper.h" + +#define AS_OBJC(context) ((__bridge DSCoinJoinWrapper *)(context)) +#define AS_RUST(context) ((__bridge void *)(context)) + +@implementation DSCoinJoinManager + +- (instancetype)initWithChainManager:(DSChainManager *)chainManager { + self = [super init]; + if (self) { + _chainManager = chainManager; + _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self.chainManager coinJoinManager:self]; + _masternodeGroup = [[DSMasternodeGroup alloc] init]; + _wrapper.masternodeGroup = _masternodeGroup; + } + return self; +} + +- (void)runCoinJoin { + if (_options != NULL) { + free(_options); + _options = NULL; + } + + _options = [self createOptions]; + + DSLog(@"[OBJ-C] CoinJoin: register"); + _walletEx = register_wallet_ex(AS_RUST(self.wrapper), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested); + _clientManager = register_client_manager(AS_RUST(self.wrapper), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); + + DSLog(@"[OBJ-C] CoinJoin: call"); + Balance *balance = [self getBalance]; + + run_client_manager(_clientManager, *balance); +// DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); +// free(balance); +} + +- (CoinJoinClientOptions *)createOptions { + CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); + options->enable_coinjoin = YES; + options->coinjoin_rounds = 1; + options->coinjoin_sessions = 1; + options->coinjoin_amount = DUFFS / 4; // 0.25 DASH + options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; + options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; + options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; + options->coinjoin_multi_session = NO; + DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.wrapper.chain.balance); + + return options; +} + +-(Balance *)getBalance { + Balance *balance = malloc(sizeof(Balance)); + balance->my_trusted = self.wrapper.chain.balance; + balance->my_immature = 0; + balance->anonymized = 0; + balance->my_untrusted_pending = 0; + balance->denominated_trusted = 0; + balance->denominated_untrusted_pending = 0; + balance->watch_only_trusted = 0; + balance->watch_only_untrusted_pending = 0; + balance->watch_only_immature = 0; + + return balance; +} + +- (void)dealloc { + if (_options != NULL) { + free(_options); + } + + unregister_client_manager(_clientManager); + unregister_wallet_ex(_walletEx); // Unregister last +} + +/// +/// MARK: Rust FFI callbacks +/// + +InputValue *getInputValueByPrevoutHash(uint8_t (*prevout_hash)[32], uint32_t index, const void *context) { + UInt256 txHash = *((UInt256 *)prevout_hash); + DSLog(@"[OBJ-C CALLBACK] CoinJoin: getInputValueByPrevoutHash"); + InputValue *inputValue = NULL; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + inputValue = malloc(sizeof(InputValue)); + DSWallet *wallet = wrapper.chain.wallets.firstObject; + int64_t value = [wallet inputValue:txHash inputIndex:index]; + + if (value != -1) { + inputValue->is_valid = TRUE; + inputValue->value = value; + } else { + inputValue->is_valid = FALSE; + } + } + + processor_destroy_block_hash(prevout_hash); + return inputValue; +} + + +bool hasChainLock(Block *block, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasChainLock"); + BOOL hasChainLock = NO; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + hasChainLock = [wrapper.chain blockHeightChainLocked:block->height]; + } + + processor_destroy_block(block); + return hasChainLock; +} + +Transaction *getTransaction(uint8_t (*tx_hash)[32], const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: getTransaction"); + UInt256 txHash = *((UInt256 *)tx_hash); + Transaction *tx = NULL; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + DSTransaction *transaction = [wrapper.chain transactionForHash:txHash]; + + if (transaction) { + tx = [transaction ffi_malloc:wrapper.chain.chainType]; + } + } + + processor_destroy_block_hash(tx_hash); + return tx; +} + +bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: isMine"); + UInt256 txHash = *((UInt256 *)tx_hash); + BOOL result = NO; + + @synchronized (context) { + result = [AS_OBJC(context) isMineInput:txHash index:index]; + } + + processor_destroy_block_hash(tx_hash); + return result; +} + +GatheredOutputs* availableCoins(bool onlySafe, CoinControl coinControl, WalletEx *walletEx, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasCollateralInputs"); + GatheredOutputs *gatheredOutputs; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + ChainType chainType = wrapper.chain.chainType; + DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:&coinControl]; + NSArray *coins = [wrapper availableCoins:walletEx onlySafe:onlySafe coinControl:cc minimumAmount:1 maximumAmount:MAX_MONEY minimumSumAmount:MAX_MONEY maximumCount:0]; + + gatheredOutputs = malloc(sizeof(GatheredOutputs)); + InputCoin **coinsArray = malloc(coins.count * sizeof(InputCoin *)); + + for (uintptr_t i = 0; i < coins.count; ++i) { + coinsArray[i] = [coins[i] ffi_malloc:chainType]; + } + + gatheredOutputs->items = coinsArray; + gatheredOutputs->item_count = (uintptr_t)coins.count; + } + + return gatheredOutputs; +} + +SelectedCoins* selectCoinsGroupedByAddresses(bool skipDenominated, bool anonymizable, bool skipUnconfirmed, int maxOupointsPerAddress, WalletEx* walletEx, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: selectCoinsGroupedByAddresses"); + SelectedCoins *vecTallyRet; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSArray *tempVecTallyRet = [wrapper selectCoinsGroupedByAddresses:walletEx skipDenominated:skipDenominated anonymizable:anonymizable skipUnconfirmed:skipUnconfirmed maxOupointsPerAddress:maxOupointsPerAddress]; + + vecTallyRet = malloc(sizeof(SelectedCoins)); + vecTallyRet->item_count = tempVecTallyRet.count; + vecTallyRet->items = malloc(tempVecTallyRet.count * sizeof(CompactTallyItem *)); + + for (uint32_t i = 0; i < tempVecTallyRet.count; i++) { + vecTallyRet->items[i] = [tempVecTallyRet[i] ffi_malloc:wrapper.chain.chainType]; + } + } + + return vecTallyRet; +} + +void destroyInputValue(InputValue *value) { + DSLog(@"[OBJ-C] CoinJoin: 💀 InputValue"); + + if (value) { + free(value); + } +} + +void destroyTransaction(Transaction *value) { + if (value) { + [DSTransaction ffi_free:value]; + } +} + +void destroySelectedCoins(SelectedCoins *selectedCoins) { + if (!selectedCoins) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 SelectedCoins"); + + if (selectedCoins->item_count > 0 && selectedCoins->items) { + for (int i = 0; i < selectedCoins->item_count; i++) { + [DSCompactTallyItem ffi_free:selectedCoins->items[i]]; + } + + free(selectedCoins->items); + } + + free(selectedCoins); +} + +void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { + if (!gatheredOutputs) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 GatheredOutputs"); + + if (gatheredOutputs->item_count > 0 && gatheredOutputs->items) { + for (int i = 0; i < gatheredOutputs->item_count; i++) { + [DSTransactionOutput ffi_free:gatheredOutputs->items[i]->output]; + free(gatheredOutputs->items[i]->outpoint_hash); + } + + free(gatheredOutputs->items); + } + + free(gatheredOutputs); +} + +Transaction* signTransaction(Transaction *transaction, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: signTransaction"); + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; + destroy_transaction(transaction); + + BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx]; + + if (isSigned) { + return [tx ffi_malloc:wrapper.chain.chainType]; + } + } + + return nil; +} + +unsigned int countInputsWithAmount(unsigned long long inputAmount, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: countInputsWithAmount"); + return [AS_OBJC(context) countInputsWithAmount:inputAmount]; +} + +ByteArray freshCoinJoinAddress(bool internal, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: freshCoinJoinAddress"); + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSString *address = [wrapper freshAddress:internal]; + + return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); +} + +bool commitTransaction(struct Recipient **items, uintptr_t item_count, const void *context) { + DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); + + NSMutableArray *amounts = [NSMutableArray array]; + NSMutableArray *scripts = [NSMutableArray array]; + + for (uintptr_t i = 0; i < item_count; i++) { + Recipient *recipient = items[i]; + [amounts addObject:@(recipient->amount)]; + NSData *script = [NSData dataWithBytes:recipient->script_pub_key.ptr length:recipient->script_pub_key.len]; + [scripts addObject:script]; + } + + // TODO: check subtract_fee_from_amount + bool result = false; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + result = [wrapper commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { + if (!error) { + // TODO: let balance_denominated_unconf = balance_info.denominated_untrusted_pending; + uint64_t balanceDenominatedUnconf = 0; + DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); + bool isFinished = finish_automatic_denominating(wrapper.manager.clientManager, balanceDenominatedUnconf, wrapper.balance_needs_anonymized); + DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); + } + }]; + } + + return result; +} + +MasternodeEntry* masternodeByHash(uint8_t (*hash)[32], const void *context) { + UInt256 mnHash = *((UInt256 *)hash); + MasternodeEntry *masternode; + + @synchronized (context) { + masternode = [[AS_OBJC(context) masternodeEntryByHash:mnHash] ffi_malloc]; + } + + return masternode; +} + +void destroyMasternodeEntry(MasternodeEntry *masternodeEntry) { + if (!masternodeEntry) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeEntry"); + [DSSimplifiedMasternodeEntry ffi_free:masternodeEntry]; +} + +uint64_t validMNCount(const void *context) { + uint64_t result = 0; + + @synchronized (context) { + result = [AS_OBJC(context) validMNCount]; + } + + return result; +} + +MasternodeList* getMNList(const void *context) { + MasternodeList *masternodes; + + @synchronized (context) { + masternodes = [[AS_OBJC(context) mnList] ffi_malloc]; + } + + return masternodes; +} + +void destroyMNList(MasternodeList *masternodeList) { // TODO: check destroyMasternodeList + if (!masternodeList) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeList"); + [DSMasternodeList ffi_free:masternodeList]; +} + +bool isBlockchainSynced(const void *context) { + BOOL result = NO; + + @synchronized (context) { + result = AS_OBJC(context).chainManager.combinedSyncProgress == 1.0; + } + + return result; +} + +bool isMasternodeOrDisconnectRequested(uint8_t (*ip_address)[16], uint16_t port, const void *context) { + BOOL result = NO; + + @synchronized (context) { + // TODO: ip_address, port + result = AS_OBJC(context).isMasternodeOrDisconnectRequested; + } + + return result; +} + +@end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 5eea9c91b..3e0af5c76 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -20,21 +20,23 @@ #import "DSTransactionOutput.h" #import "DSCoinControl.h" #import "DSCompactTallyItem.h" +#import "DSCoinJoinManager.h" +#import "DSMasternodeGroup.h" NS_ASSUME_NONNULL_BEGIN @interface DSCoinJoinWrapper : NSObject -// TODO: free on dealloc -@property (strong, nonatomic, readonly) DSChainManager *chainManager; -@property (nonatomic, assign, nullable) CoinJoinClientSession *clientSession; +@property (nonatomic, assign, nullable) DSChainManager *chainManager; +@property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; @property (nonatomic, assign) uint64_t balance_needs_anonymized; @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong) DSChain *chain; +@property (nonatomic, weak, nullable) DSCoinJoinManager *manager; -- (instancetype)initWithChainManager:(DSChainManager *)chainManager; +- (instancetype)initWithManagers:(DSChainManager *)chainManager coinJoinManager: (DSCoinJoinManager*)coinJoinMnager; - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; - (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; @@ -45,6 +47,7 @@ NS_ASSUME_NONNULL_BEGIN - (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; - (uint64_t)validMNCount; - (DSMasternodeList *)mnList; +- (BOOL)isMasternodeOrDisconnectRequested; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index f512de804..166cf639b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -32,10 +32,11 @@ @implementation DSCoinJoinWrapper -- (instancetype)initWithChainManager:(DSChainManager *)chainManager { +- (instancetype)initWithManagers:(DSChainManager *)chainManager coinJoinManager: (DSCoinJoinManager*)coinJoinMnager { self = [super init]; if (self) { _chainManager = chainManager; + _manager = coinJoinMnager; } return self; } @@ -404,5 +405,8 @@ - (DSMasternodeList *)mnList { return self.chainManager.masternodeManager.currentMasternodeList; } -@end +- (BOOL)isMasternodeOrDisconnectRequested { + return [_masternodeGroup isMasternodeOrDisconnectRequested]; +} +@end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.h b/DashSync/shared/Models/CoinJoin/Model/DSCoinControl.h similarity index 100% rename from DashSync/shared/Models/CoinJoin/DSCoinControl.h rename to DashSync/shared/Models/CoinJoin/Model/DSCoinControl.h diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.m b/DashSync/shared/Models/CoinJoin/Model/DSCoinControl.m similarity index 100% rename from DashSync/shared/Models/CoinJoin/DSCoinControl.m rename to DashSync/shared/Models/CoinJoin/Model/DSCoinControl.m diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h new file mode 100644 index 000000000..d21fce17a --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -0,0 +1,28 @@ +// +// Created by Andrei Ashikhmin +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface DSMasternodeGroup : NSObject + +-(BOOL)isMasternodeOrDisconnectRequested; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m new file mode 100644 index 000000000..a6e26764e --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -0,0 +1,27 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSMasternodeGroup.h" + +@implementation DSMasternodeGroup + +-(BOOL)isMasternodeOrDisconnectRequested { + // TODO: + return NO; +} + +@end diff --git a/Example/DashSync/DSCoinJoinViewController.h b/Example/DashSync/DSCoinJoinViewController.h index 9ce1aa9bd..5ad2e2c4f 100644 --- a/Example/DashSync/DSCoinJoinViewController.h +++ b/Example/DashSync/DSCoinJoinViewController.h @@ -16,19 +16,16 @@ // #import -#import "DSCoinJoinWrapper.h" +#import "DSCoinJoinManager.h" NS_ASSUME_NONNULL_BEGIN @interface DSCoinJoinViewController : UIViewController -@property (nonatomic, strong) DSChainManager *chainManager; +@property (strong, nonatomic) DSChainManager *chainManager; @property (strong, nonatomic) IBOutlet UISwitch *coinJoinSwitch; @property (strong, nonatomic) IBOutlet UILabel *infoLabel; -@property (nonatomic, assign, nullable) CoinJoin *coinJoin; -@property (nonatomic, strong) DSCoinJoinWrapper *wrapper; -@property (nonatomic, assign, nullable) CoinJoinClientSession *clientSession; -@property (nonatomic, assign, nullable) CoinJoinClientOptions *options; +@property (strong, nonatomic) DSCoinJoinManager *coinJoinManager; @end diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index 97c9cef52..b151bed90 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -18,15 +18,7 @@ #import "DSCoinJoinViewController.h" #import "DSChainManager.h" #import "NSString+Dash.h" -#import "DSTransaction+CoinJoin.h" -#import "DSTransactionOutput+CoinJoin.h" -#import "DSCoinControl.h" -#import "DSCoinJoinWrapper.h" -#import "DSSimplifiedMasternodeEntry+Mndiff.h" -#import "DSMasternodeList+Mndiff.h" - -#define AS_OBJC(context) ((__bridge DSCoinJoinWrapper *)(context)) -#define AS_RUST(context) ((__bridge void *)(context)) +#import "DSCoinJoinManager.h" @implementation DSCoinJoinViewController @@ -50,337 +42,14 @@ - (void)viewDidDisappear:(BOOL)animated { } - (void)startCoinJoin { - // TODO: init parameters // TODO: subscribe // TODO: refreshUnusedKeys() - if (_wrapper == NULL) { - _wrapper = [[DSCoinJoinWrapper alloc] initWithChainManager:self.chainManager]; - } - - [self runCoinJoin]; -} - --(void)runCoinJoin { - if (_options != NULL) { - free(_options); - } - - _options = malloc(sizeof(CoinJoinClientOptions)); - _options->enable_coinjoin = YES; - _options->coinjoin_rounds = 1; - _options->coinjoin_sessions = 1; - _options->coinjoin_amount = DUFFS / 4; // 0.25 DASH - _options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; - _options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; - _options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; - _options->coinjoin_multi_session = NO; - - if (_coinJoin == NULL) { - DSLog(@"[OBJ-C] CoinJoin: register"); - _coinJoin = register_coinjoin(getInputValueByPrevoutHash, hasChainLock, destroyInputValue, AS_RUST(self.wrapper)); - } - - if (_clientSession == NULL) { - _clientSession = register_client_session(_coinJoin, _options, getTransaction, destroyTransaction, isMineInput, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, signTransaction, countInputsWithAmount, freshCoinJoinAddress, commitTransaction, masternodeByHash, destroyMasternodeEntry, AS_RUST(self.wrapper)); - self.wrapper.clientSession = _clientSession; - } - - DSLog(@"[OBJ-C] CoinJoin: call"); - Balance *balance = malloc(sizeof(Balance)); - balance->my_trusted = self.wrapper.chain.balance; - balance->my_immature = 0; - balance->anonymized = 0; - balance->my_untrusted_pending = 0; - balance->denominated_trusted = 0; - balance->denominated_untrusted_pending = 0; - balance->watch_only_trusted = 0; - balance->watch_only_untrusted_pending = 0; - balance->watch_only_immature = 0; - DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.wrapper.chain.balance); - self.wrapper.balance_needs_anonymized = do_automatic_denominating(_clientSession, *balance); - DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); - free(balance); -} - - -/// -/// MARK: Rust FFI callbacks -/// - -InputValue *getInputValueByPrevoutHash(uint8_t (*prevout_hash)[32], uint32_t index, const void *context) { - UInt256 txHash = *((UInt256 *)prevout_hash); - DSLog(@"[OBJ-C CALLBACK] CoinJoin: getInputValueByPrevoutHash"); - InputValue *inputValue = NULL; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - inputValue = malloc(sizeof(InputValue)); - DSWallet *wallet = wrapper.chain.wallets.firstObject; - int64_t value = [wallet inputValue:txHash inputIndex:index]; - - if (value != -1) { - inputValue->is_valid = TRUE; - inputValue->value = value; - } else { - inputValue->is_valid = FALSE; - } - } - - processor_destroy_block_hash(prevout_hash); - return inputValue; -} - - -bool hasChainLock(Block *block, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasChainLock"); - BOOL hasChainLock = NO; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - hasChainLock = [wrapper.chain blockHeightChainLocked:block->height]; - } - - processor_destroy_block(block); - return hasChainLock; -} - -Transaction *getTransaction(uint8_t (*tx_hash)[32], const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: getTransaction"); - UInt256 txHash = *((UInt256 *)tx_hash); - Transaction *tx = NULL; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - DSTransaction *transaction = [wrapper.chain transactionForHash:txHash]; - - if (transaction) { - tx = [transaction ffi_malloc:wrapper.chain.chainType]; - } - } - - processor_destroy_block_hash(tx_hash); - return tx; -} - -bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: isMine"); - UInt256 txHash = *((UInt256 *)tx_hash); - BOOL result = NO; - - @synchronized (context) { - result = [AS_OBJC(context) isMineInput:txHash index:index]; - } - - processor_destroy_block_hash(tx_hash); - return result; -} - -GatheredOutputs* availableCoins(bool onlySafe, CoinControl coinControl, WalletEx *walletEx, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasCollateralInputs"); - GatheredOutputs *gatheredOutputs; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - ChainType chainType = wrapper.chain.chainType; - DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:&coinControl]; - NSArray *coins = [wrapper availableCoins:walletEx onlySafe:onlySafe coinControl:cc minimumAmount:1 maximumAmount:MAX_MONEY minimumSumAmount:MAX_MONEY maximumCount:0]; - - gatheredOutputs = malloc(sizeof(GatheredOutputs)); - InputCoin **coinsArray = malloc(coins.count * sizeof(InputCoin *)); - - for (uintptr_t i = 0; i < coins.count; ++i) { - coinsArray[i] = [coins[i] ffi_malloc:chainType]; - } - - gatheredOutputs->items = coinsArray; - gatheredOutputs->item_count = (uintptr_t)coins.count; - } - - return gatheredOutputs; -} - -SelectedCoins* selectCoinsGroupedByAddresses(bool skipDenominated, bool anonymizable, bool skipUnconfirmed, int maxOupointsPerAddress, WalletEx* walletEx, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: selectCoinsGroupedByAddresses"); - SelectedCoins *vecTallyRet; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - NSArray *tempVecTallyRet = [wrapper selectCoinsGroupedByAddresses:walletEx skipDenominated:skipDenominated anonymizable:anonymizable skipUnconfirmed:skipUnconfirmed maxOupointsPerAddress:maxOupointsPerAddress]; - - vecTallyRet = malloc(sizeof(SelectedCoins)); - vecTallyRet->item_count = tempVecTallyRet.count; - vecTallyRet->items = malloc(tempVecTallyRet.count * sizeof(CompactTallyItem *)); - - for (uint32_t i = 0; i < tempVecTallyRet.count; i++) { - vecTallyRet->items[i] = [tempVecTallyRet[i] ffi_malloc:wrapper.chain.chainType]; - } - } - - return vecTallyRet; -} - -void destroyInputValue(InputValue *value) { - DSLog(@"[OBJ-C] CoinJoin: 💀 InputValue"); - - if (value) { - free(value); - } -} - -void destroyTransaction(Transaction *value) { - if (value) { - [DSTransaction ffi_free:value]; - } -} - -void destroySelectedCoins(SelectedCoins *selectedCoins) { - if (!selectedCoins) { - return; - } - - DSLog(@"[OBJ-C] CoinJoin: 💀 SelectedCoins"); - - if (selectedCoins->item_count > 0 && selectedCoins->items) { - for (int i = 0; i < selectedCoins->item_count; i++) { - [DSCompactTallyItem ffi_free:selectedCoins->items[i]]; - } - - free(selectedCoins->items); - } - - free(selectedCoins); -} - -void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { - if (!gatheredOutputs) { - return; - } - - DSLog(@"[OBJ-C] CoinJoin: 💀 GatheredOutputs"); - - if (gatheredOutputs->item_count > 0 && gatheredOutputs->items) { - for (int i = 0; i < gatheredOutputs->item_count; i++) { - [DSTransactionOutput ffi_free:gatheredOutputs->items[i]->output]; - free(gatheredOutputs->items[i]->outpoint_hash); - } - - free(gatheredOutputs->items); - } - - free(gatheredOutputs); -} - -Transaction* signTransaction(Transaction *transaction, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: signTransaction"); - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; - destroy_transaction(transaction); - - BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx]; - - if (isSigned) { - return [tx ffi_malloc:wrapper.chain.chainType]; - } - } - - return nil; -} - -unsigned int countInputsWithAmount(unsigned long long inputAmount, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: countInputsWithAmount"); - return [AS_OBJC(context) countInputsWithAmount:inputAmount]; -} - -ByteArray freshCoinJoinAddress(bool internal, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: freshCoinJoinAddress"); - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - NSString *address = [wrapper freshAddress:internal]; - - return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); -} - -bool commitTransaction(struct Recipient **items, uintptr_t item_count, const void *context) { - DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); - - NSMutableArray *amounts = [NSMutableArray array]; - NSMutableArray *scripts = [NSMutableArray array]; - - for (uintptr_t i = 0; i < item_count; i++) { - Recipient *recipient = items[i]; - [amounts addObject:@(recipient->amount)]; - NSData *script = [NSData dataWithBytes:recipient->script_pub_key.ptr length:recipient->script_pub_key.len]; - [scripts addObject:script]; - } - - // TODO: check subtract_fee_from_amount - bool result = false; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - result = [wrapper commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { - if (!error) { - // TODO: let balance_denominated_unconf = balance_info.denominated_untrusted_pending; - uint64_t balanceDenominatedUnconf = 0; - DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); - bool isFinished = finish_automatic_denominating(wrapper.clientSession, balanceDenominatedUnconf, wrapper.balance_needs_anonymized); - DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); - } - }]; - } - - return result; -} - -MasternodeEntry* masternodeByHash(uint8_t (*hash)[32], const void *context) { - UInt256 mnHash = *((UInt256 *)hash); - MasternodeEntry *masternode; - - @synchronized (context) { - masternode = [[AS_OBJC(context) masternodeEntryByHash:mnHash] ffi_malloc]; - } - - return masternode; -} - -void destroyMasternodeEntry(MasternodeEntry *masternodeEntry) { - if (!masternodeEntry) { - return; - } - - DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeEntry"); - [DSSimplifiedMasternodeEntry ffi_free:masternodeEntry]; -} - -uint64_t validMNCount(const void *context) { - uint64_t result = 0; - - @synchronized (context) { - result = [AS_OBJC(context) validMNCount]; - } - - return result; -} - -MasternodeList* getMNList(const void *context) { - MasternodeList *masternodes; - - @synchronized (context) { - masternodes = [[AS_OBJC(context) mnList] ffi_malloc]; - } - - return masternodes; -} - -void destroyMNList(MasternodeList *masternodeList) { - if (!masternodeList) { - return; + if (_coinJoinManager == NULL) { + _coinJoinManager = [[DSCoinJoinManager alloc] initWithChainManager:self.chainManager]; } - DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeList"); - [DSMasternodeList ffi_free:masternodeList]; + [_coinJoinManager runCoinJoin]; } @end From 5e65d7158130182f3e77ee91dca411730b3cc8c6 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 19 Apr 2024 12:35:35 +0700 Subject: [PATCH 014/133] feat: send accept message --- DashSync.podspec | 2 +- .../Models/CoinJoin/DSCoinJoinManager.m | 28 ++++++++++++- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 11 +++++- .../Managers/Chain Managers/DSPeerManager.h | 1 + .../CoinJoin/DSCoinJoinAcceptMessage.h | 31 +++++++++++++++ .../CoinJoin/DSCoinJoinAcceptMessage.m | 39 +++++++++++++++++++ DashSync/shared/Models/Network/DSPeer.h | 4 +- DashSync/shared/Models/Network/DSPeer.m | 8 ++-- Example/Podfile.lock | 3 +- 10 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m diff --git a/DashSync.podspec b/DashSync.podspec index 98e8e450b..f5b523efc 100644 --- a/DashSync.podspec +++ b/DashSync.podspec @@ -34,7 +34,7 @@ Pod::Spec.new do |s| s.ios.framework = 'UIKit' s.macos.framework = 'Cocoa' s.compiler_flags = '-Wno-comma' - s.dependency 'DashSharedCore', '0.4.16' + #s.dependency 'DashSharedCore', '0.4.16' s.dependency 'CocoaLumberjack', '3.7.2' s.ios.dependency 'DWAlertController', '0.2.1' s.dependency 'DSDynamicOptions', '0.1.2' diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 531b87ae7..6ac52e099 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -50,7 +50,7 @@ - (void)runCoinJoin { _options = [self createOptions]; DSLog(@"[OBJ-C] CoinJoin: register"); - _walletEx = register_wallet_ex(AS_RUST(self.wrapper), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested); + _walletEx = register_wallet_ex(AS_RUST(self.wrapper), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage); _clientManager = register_client_manager(AS_RUST(self.wrapper), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); DSLog(@"[OBJ-C] CoinJoin: call"); @@ -59,6 +59,20 @@ - (void)runCoinJoin { run_client_manager(_clientManager, *balance); // DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); // free(balance); + + + // Might be useful: +// - (DSPeer *)peerForLocation:(UInt128)IPAddress port:(uint16_t)port + +// if ([self.masternodeManager hasMasternodeAtLocation:IPAddress port:port]) { +// return DSPeerType_MasterNode; +// } else { +// return DSPeerType_FullNode; +// } + +// - (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry + +// - (void)sendRequest:(DSMessageRequest *)request } - (CoinJoinClientOptions *)createOptions { @@ -400,4 +414,16 @@ bool isMasternodeOrDisconnectRequested(uint8_t (*ip_address)[16], uint16_t port, return result; } +bool sendMessage(ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, const void *context) { + UInt128 ipAddress = *((UInt128 *)ip_address); + BOOL result = YES; // TODO + + @synchronized (context) { + NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; + [AS_OBJC(context) sendAcceptMessage:message withPeerIP:ipAddress port:port]; + } + + return result; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 3e0af5c76..0f6b2f464 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -48,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN - (uint64_t)validMNCount; - (DSMasternodeList *)mnList; - (BOOL)isMasternodeOrDisconnectRequested; +- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 166cf639b..049c372db 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -26,6 +26,8 @@ #import "DSChainManager.h" #import "DSTransactionManager.h" #import "DSMasternodeManager.h" +#import "DSCoinJoinAcceptMessage.h" +#import "DSPeerManager.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @@ -292,7 +294,7 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { continue; } - if (![account containsAddress:output.address]) { + if (output.address == nil || ![account containsAddress:output.address]) { continue; } @@ -409,4 +411,11 @@ - (BOOL)isMasternodeOrDisconnectRequested { return [_masternodeGroup isMasternodeOrDisconnectRequested]; } +- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { + DSCoinJoinAcceptMessage *request = [[DSCoinJoinAcceptMessage alloc] initWithData:message]; + DSPeer *peer = [self.chainManager.peerManager peerForLocation:address port:port]; + [peer sendRequest:request]; +// [self.chainManager.peerManager sendRequest:request]; +} + @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h index 174409623..9f6938521 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h @@ -76,6 +76,7 @@ typedef NS_ENUM(uint16_t, DSDisconnectReason) @property (nonatomic, readonly) NSArray *registeredDevnetPeerServices; @property (nullable, nonatomic, readonly) NSString *trustedPeerHost; +- (DSPeer *)peerForLocation:(UInt128)IPAddress port:(uint16_t)port; - (DSPeerStatus)statusForLocation:(UInt128)IPAddress port:(uint32_t)port; - (DSPeerType)typeForLocation:(UInt128)IPAddress port:(uint32_t)port; - (void)setTrustedPeerHost:(NSString *_Nullable)host; diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h new file mode 100644 index 000000000..808318959 --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h @@ -0,0 +1,31 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSMessageRequest.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DSCoinJoinAcceptMessage : DSMessageRequest + +@property (nonatomic, readonly) NSData *data; + +- (instancetype)initWithData:(NSData *)data; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m new file mode 100644 index 000000000..117e1cf73 --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m @@ -0,0 +1,39 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSCoinJoinAcceptMessage.h" +#import "DSPeer.h" + +@implementation DSCoinJoinAcceptMessage + +- (instancetype)initWithData:(NSData *)data { + self = [super init]; + if (self) { + _data = data; + } + return self; +} + +- (NSString *)type { + return MSG_COINJOIN_ACCEPT; +} + +- (NSData *)toData { + return self.data; +} +@end diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index db8421e13..845cf2841 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -137,9 +137,9 @@ typedef NS_ENUM(uint32_t, DSInvType) #define MSG_GOVOBJVOTE @"govobjvote" #define MSG_GOVOBJSYNC @"govsync" -//Private send +// CoinJoin -#define MSG_DARKSENDANNOUNCE @"dsa" +#define MSG_COINJOIN_ACCEPT @"dsa" #define MSG_DARKSENDCONTROL @"dsc" #define MSG_DARKSENDFINISH @"dsf" #define MSG_DARKSENDINITIATE @"dsi" diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 1f7f9ee61..4c5988ee9 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -843,9 +843,7 @@ - (void)acceptMessage:(NSData *)message type:(NSString *)type { //else if ([MSG_GOVOBJSYNC isEqual:type]) [self acceptGovObjectSyncMessage:message]; //private send - else if ([MSG_DARKSENDANNOUNCE isEqual:type]) - [self acceptDarksendAnnounceMessage:message]; - else if ([MSG_DARKSENDCONTROL isEqual:type]) + if ([MSG_DARKSENDCONTROL isEqual:type]) [self acceptDarksendControlMessage:message]; else if ([MSG_DARKSENDFINISH isEqual:type]) [self acceptDarksendFinishMessage:message]; @@ -1824,21 +1822,25 @@ - (void)acceptDarksendAnnounceMessage:(NSData *)message { } - (void)acceptDarksendControlMessage:(NSData *)message { + DSLog(@"[OBJ-C] CoinJoin: got dsc"); } - (void)acceptDarksendFinishMessage:(NSData *)message { + DSLog(@"[OBJ-C] CoinJoin: got dsf"); } - (void)acceptDarksendInitiateMessage:(NSData *)message { } - (void)acceptDarksendQuorumMessage:(NSData *)message { + DSLog(@"[OBJ-C] CoinJoin: got dsq"); } - (void)acceptDarksendSessionMessage:(NSData *)message { } - (void)acceptDarksendSessionUpdateMessage:(NSData *)message { + DSLog(@"[OBJ-C] CoinJoin: got dssu"); } - (void)acceptDarksendTransactionMessage:(NSData *)message { diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 702636220..2455f529c 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -590,7 +590,6 @@ PODS: - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) - - DashSharedCore (= 0.4.16) - DSDynamicOptions (= 0.1.2) - DWAlertController (= 0.2.1) - TinyCborObjc (= 0.4.6) @@ -715,7 +714,7 @@ SPEC CHECKSUMS: CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b - DashSync: 5c4dea6e4ef83df33f23f85b7f2b97ef6843de87 + DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f From eecc368fbe32fd03fb441575ed7e4370bbad829a Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 22 Apr 2024 20:17:40 +0700 Subject: [PATCH 015/133] chore: switching manager and wrapper --- .../Models/CoinJoin/DSCoinJoinManager.h | 38 +- .../Models/CoinJoin/DSCoinJoinManager.m | 685 ++++++++++-------- .../Models/CoinJoin/DSCoinJoinWrapper.h | 37 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 665 ++++++++--------- 4 files changed, 741 insertions(+), 684 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 3d01acbcd..0234c816c 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -16,25 +16,41 @@ // #import +#import "DSChain.h" +#import "DSTransactionOutput.h" +#import "DSCoinControl.h" +#import "DSCompactTallyItem.h" +#import "DSCoinJoinManager.h" +#import "DSCoinJoinWrapper.h" #import "DSMasternodeGroup.h" -#import "DSChainManager.h" NS_ASSUME_NONNULL_BEGIN -@class DSCoinJoinWrapper; - @interface DSCoinJoinManager : NSObject -@property (nonatomic, strong, nullable) DSChainManager *chainManager; -@property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; +@property (nonatomic, assign, nullable) DSChainManager *chainManager; @property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; - -@property (nonatomic, assign, nullable) WalletEx *walletEx; -@property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; - -- (instancetype)initWithChainManager:(DSChainManager *)chainManager; -- (void)runCoinJoin; +@property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; +@property (nonatomic, assign) BOOL anonymizableTallyCached; +@property (nonatomic, strong) DSChain *chain; +@property (nonatomic, weak, nullable) DSCoinJoinWrapper *wrapper; + +- (instancetype)initWithWrapper:(DSCoinJoinWrapper *)coinJoinWrapper chainManager:(DSChainManager *)chainManager; + +- (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; +- (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; +- (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; +- (uint32_t)countInputsWithAmount:(uint64_t)inputAmount; +- (NSString *)freshAddress:(BOOL)internal; +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished; +- (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; +- (uint64_t)validMNCount; +- (DSMasternodeList *)mnList; +- (BOOL)isMasternodeOrDisconnectRequested; +- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; +- (uint64_t)getDenominatedBalance; +- (uint64_t)getAnonymizedBalance; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 6ac52e099..2dadfec3e 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -16,414 +16,457 @@ // #import "DSCoinJoinManager.h" -#import "DSWallet.h" -#import "DSTransaction+CoinJoin.h" -#import "DSTransactionOutput+CoinJoin.h" -#import "DSSimplifiedMasternodeEntry+Mndiff.h" -#import "DSMasternodeList+Mndiff.h" +#import "DSTransaction.h" +#import "DSTransactionOutput.h" #import "DSAccount.h" +#import "DSCoinControl.h" +#import "DSWallet.h" +#import "BigIntTypes.h" +#import "NSString+Bitcoin.h" #import "DSChainManager.h" -#import "DSCoinJoinWrapper.h" +#import "DSTransactionManager.h" +#import "DSMasternodeManager.h" +#import "DSCoinJoinAcceptMessage.h" +#import "DSPeerManager.h" -#define AS_OBJC(context) ((__bridge DSCoinJoinWrapper *)(context)) -#define AS_RUST(context) ((__bridge void *)(context)) +int32_t const DEFAULT_MIN_DEPTH = 0; +int32_t const DEFAULT_MAX_DEPTH = 9999999; @implementation DSCoinJoinManager -- (instancetype)initWithChainManager:(DSChainManager *)chainManager { +- (instancetype)initWithWrapper:(DSCoinJoinWrapper *)coinJoinWrapper chainManager:(DSChainManager *)chainManager { self = [super init]; if (self) { _chainManager = chainManager; - _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self.chainManager coinJoinManager:self]; - _masternodeGroup = [[DSMasternodeGroup alloc] init]; - _wrapper.masternodeGroup = _masternodeGroup; + _wrapper = coinJoinWrapper; } return self; } -- (void)runCoinJoin { - if (_options != NULL) { - free(_options); - _options = NULL; - } - - _options = [self createOptions]; - - DSLog(@"[OBJ-C] CoinJoin: register"); - _walletEx = register_wallet_ex(AS_RUST(self.wrapper), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage); - _clientManager = register_client_manager(AS_RUST(self.wrapper), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); - - DSLog(@"[OBJ-C] CoinJoin: call"); - Balance *balance = [self getBalance]; - - run_client_manager(_clientManager, *balance); -// DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); -// free(balance); - - - // Might be useful: -// - (DSPeer *)peerForLocation:(UInt128)IPAddress port:(uint16_t)port - -// if ([self.masternodeManager hasMasternodeAtLocation:IPAddress port:port]) { -// return DSPeerType_MasterNode; -// } else { -// return DSPeerType_FullNode; -// } - -// - (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry - -// - (void)sendRequest:(DSMessageRequest *)request -} - -- (CoinJoinClientOptions *)createOptions { - CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); - options->enable_coinjoin = YES; - options->coinjoin_rounds = 1; - options->coinjoin_sessions = 1; - options->coinjoin_amount = DUFFS / 4; // 0.25 DASH - options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; - options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; - options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; - options->coinjoin_multi_session = NO; - DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.wrapper.chain.balance); - - return options; +- (DSChain *)chain { + return self.chainManager.chain; } --(Balance *)getBalance { - Balance *balance = malloc(sizeof(Balance)); - balance->my_trusted = self.wrapper.chain.balance; - balance->my_immature = 0; - balance->anonymized = 0; - balance->my_untrusted_pending = 0; - balance->denominated_trusted = 0; - balance->denominated_untrusted_pending = 0; - balance->watch_only_trusted = 0; - balance->watch_only_untrusted_pending = 0; - balance->watch_only_immature = 0; +- (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { + DSTransaction *tx = [self.chain transactionForHash:txHash]; + DSAccount *account = [self.chain firstAccountThatCanContainTransaction:tx]; - return balance; -} - -- (void)dealloc { - if (_options != NULL) { - free(_options); + if (index < tx.outputs.count) { + DSTransactionOutput *output = tx.outputs[index]; + + if ([account containsAddress:output.address]) { // TODO: is it the same as isPubKeyMine? + return YES; + } } - unregister_client_manager(_clientManager); - unregister_wallet_ex(_walletEx); // Unregister last + return NO; } -/// -/// MARK: Rust FFI callbacks -/// - -InputValue *getInputValueByPrevoutHash(uint8_t (*prevout_hash)[32], uint32_t index, const void *context) { - UInt256 txHash = *((UInt256 *)prevout_hash); - DSLog(@"[OBJ-C CALLBACK] CoinJoin: getInputValueByPrevoutHash"); - InputValue *inputValue = NULL; +- (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress { - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - inputValue = malloc(sizeof(InputValue)); - DSWallet *wallet = wrapper.chain.wallets.firstObject; - int64_t value = [wallet inputValue:txHash inputIndex:index]; + @synchronized(self) { + // Note: cache is checked in dash-shared-core. + + uint64_t smallestDenom = coinjoin_get_smallest_denomination(); + NSMutableDictionary *mapTally = [[NSMutableDictionary alloc] init]; + NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; + + DSUTXO outpoint; + NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; + for (NSValue *value in utxos) { + [value getValue:&outpoint]; + + if ([setWalletTxesCounted containsObject:uint256_data(outpoint.hash)]) { + continue; + } + + [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; + DSTransaction *wtx = [self.chain transactionForHash:outpoint.hash]; + + if (wtx == nil) { + continue; + } + + if (wtx.isCoinbaseClassicTransaction && [wtx getBlocksToMaturity] > 0) { + continue; + } + + DSAccount *account = [self.chain firstAccountThatCanContainTransaction:wtx]; + BOOL isTrusted = wtx.instantSendReceived || [account transactionIsVerified:wtx]; + + if (skipUnconfirmed && !isTrusted) { + continue; + } - if (value != -1) { - inputValue->is_valid = TRUE; - inputValue->value = value; - } else { - inputValue->is_valid = FALSE; + for (int32_t i = 0; i < wtx.outputs.count; i++) { + DSTransactionOutput *output = wtx.outputs[i]; + NSData *txDest = output.outScript; + NSString *address = [NSString bitcoinAddressWithScriptPubKey:txDest forChain:self.chain]; + + if (address == nil) { + continue; + } + + if (![account containsAddress:output.address]) { // TODO: is it the same as isPubKeyMine? + continue; + } + + DSCompactTallyItem *tallyItem = mapTally[txDest]; + + if (maxOupointsPerAddress != -1 && tallyItem != nil && tallyItem.inputCoins.count >= maxOupointsPerAddress) { + continue; + } + + if (is_locked_coin(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { + continue; + } + + if (skipDenominated && is_denominated_amount(output.amount)) { + continue; + } + + if (anonymizable) { + // ignore collaterals + if (is_collateral_amount(output.amount)) { + continue; + } + + // ignore outputs that are 10 times smaller then the smallest denomination + // otherwise they will just lead to higher fee / lower priority + if (output.amount <= smallestDenom/10) { + continue; + } + + // ignore mixed + if (is_fully_mixed(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { + continue; + } + } + + if (tallyItem == nil) { + tallyItem = [[DSCompactTallyItem alloc] init]; + tallyItem.txDestination = txDest; + mapTally[txDest] = tallyItem; + } + + tallyItem.amount += output.amount; + DSInputCoin *coin = [[DSInputCoin alloc] initWithTx:wtx index:i]; + [tallyItem.inputCoins addObject:coin]; + } } - } - - processor_destroy_block_hash(prevout_hash); - return inputValue; -} - - -bool hasChainLock(Block *block, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasChainLock"); - BOOL hasChainLock = NO; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - hasChainLock = [wrapper.chain blockHeightChainLocked:block->height]; - } - - processor_destroy_block(block); - return hasChainLock; -} - -Transaction *getTransaction(uint8_t (*tx_hash)[32], const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: getTransaction"); - UInt256 txHash = *((UInt256 *)tx_hash); - Transaction *tx = NULL; - - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - DSTransaction *transaction = [wrapper.chain transactionForHash:txHash]; - if (transaction) { - tx = [transaction ffi_malloc:wrapper.chain.chainType]; + // construct resulting vector + // NOTE: vecTallyRet is "sorted" by txdest (i.e. address), just like mapTally + NSMutableArray *vecTallyRet = [[NSMutableArray alloc] init]; + + for (DSCompactTallyItem *item in mapTally.allValues) { + // TODO: (dashj) ignore this to get this dust back in + if (anonymizable && item.amount < smallestDenom) { + continue; + } + + [vecTallyRet addObject:item]; } - } - - processor_destroy_block_hash(tx_hash); - return tx; -} -bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: isMine"); - UInt256 txHash = *((UInt256 *)tx_hash); - BOOL result = NO; - - @synchronized (context) { - result = [AS_OBJC(context) isMineInput:txHash index:index]; + // Note: cache is assigned in dash-shared-core + + return vecTallyRet; } - - processor_destroy_block_hash(tx_hash); - return result; } -GatheredOutputs* availableCoins(bool onlySafe, CoinControl coinControl, WalletEx *walletEx, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasCollateralInputs"); - GatheredOutputs *gatheredOutputs; +- (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { + uint32_t total = 0; - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - ChainType chainType = wrapper.chain.chainType; - DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:&coinControl]; - NSArray *coins = [wrapper availableCoins:walletEx onlySafe:onlySafe coinControl:cc minimumAmount:1 maximumAmount:MAX_MONEY minimumSumAmount:MAX_MONEY maximumCount:0]; - - gatheredOutputs = malloc(sizeof(GatheredOutputs)); - InputCoin **coinsArray = malloc(coins.count * sizeof(InputCoin *)); + @synchronized(self) { + NSArray *unspent = self.chain.wallets.firstObject.unspentOutputs; - for (uintptr_t i = 0; i < coins.count; ++i) { - coinsArray[i] = [coins[i] ffi_malloc:chainType]; + DSUTXO outpoint; + for (uint32_t i = 0; i < unspent.count; i++) { + [unspent[i] getValue:&outpoint]; + DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; + + if (tx == NULL) { + continue; + } + + if (tx.outputs[outpoint.n].amount != inputAmount) { + continue; + } + + if (tx.confirmations < 0) { + continue; + } + + total++; } - - gatheredOutputs->items = coinsArray; - gatheredOutputs->item_count = (uintptr_t)coins.count; } - return gatheredOutputs; + return total; } -SelectedCoins* selectCoinsGroupedByAddresses(bool skipDenominated, bool anonymizable, bool skipUnconfirmed, int maxOupointsPerAddress, WalletEx* walletEx, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: selectCoinsGroupedByAddresses"); - SelectedCoins *vecTallyRet; +- (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount { + NSMutableArray *vCoins = [NSMutableArray array]; - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - NSArray *tempVecTallyRet = [wrapper selectCoinsGroupedByAddresses:walletEx skipDenominated:skipDenominated anonymizable:anonymizable skipUnconfirmed:skipUnconfirmed maxOupointsPerAddress:maxOupointsPerAddress]; - - vecTallyRet = malloc(sizeof(SelectedCoins)); - vecTallyRet->item_count = tempVecTallyRet.count; - vecTallyRet->items = malloc(tempVecTallyRet.count * sizeof(CompactTallyItem *)); + @synchronized(self) { + CoinType coinType = coinControl != nil ? coinControl.coinType : CoinType_AllCoins; + + uint64_t total = 0; + // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses + BOOL allowUsedAddresses = /* !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || */ (coinControl != nil && !coinControl.avoidAddressReuse); + int32_t minDepth = coinControl != nil ? coinControl.minDepth : DEFAULT_MIN_DEPTH; + int32_t maxDepth = coinControl != nil ? coinControl.maxDepth : DEFAULT_MAX_DEPTH; - for (uint32_t i = 0; i < tempVecTallyRet.count; i++) { - vecTallyRet->items[i] = [tempVecTallyRet[i] ffi_malloc:wrapper.chain.chainType]; + for (DSTransaction *coin in [self getSpendableTXs]) { + UInt256 wtxid = coin.txHash; + DSAccount *account = [self.chain firstAccountThatCanContainTransaction:coin]; + + if ([account transactionIsPending:coin]) { + continue; + } + + if (coin.isImmatureCoinBase) { + continue; + } + + BOOL safeTx = coin.instantSendReceived || [account transactionIsVerified:coin]; + + if (onlySafe && !safeTx) { + continue; + } + + uint32_t depth = coin.confirmations; + + if (depth < minDepth || depth > maxDepth) { + continue; + } + + for (uint32_t i = 0; i < coin.outputs.count; i++) { + DSTransactionOutput *output = coin.outputs[i]; + DSLog(@"[OBJ-C] CoinJoin: check output: %llu", output.amount); + uint64_t value = output.amount; + BOOL found = NO; + + if (coinType == CoinType_OnlyFullyMixed) { + if (!is_denominated_amount(value)) { + continue; + } + + found = is_fully_mixed(walletEx, (uint8_t (*)[32])(wtxid.u8), (uint32_t)i); + } else if (coinType == CoinType_OnlyReadyToMix) { + if (!is_denominated_amount(value)) { + continue; + } + + found = !is_fully_mixed(walletEx, (uint8_t (*)[32])(wtxid.u8), (uint32_t)i); + } else if (coinType == CoinType_OnlyNonDenominated) { + if (is_collateral_amount(value)) { + continue; // do not use collateral amounts + } + + found = !is_denominated_amount(value); + } else if (coinType == CoinType_OnlyMasternodeCollateral) { + found = value == 1000 * DUFFS; + } else if (coinType == CoinType_OnlyCoinJoinCollateral) { + found = is_collateral_amount(value); + } else { + found = YES; + } + + if (!found) { + continue; + } + + if (value < minimumAmount || value > maximumAmount) { + continue; + } + + NSValue *outputValue = dsutxo_obj(((DSUTXO){wtxid, i})); + + if (coinControl != nil && coinControl.hasSelected && !coinControl.allowOtherInputs && ![coinControl isSelected:outputValue]) { + continue; + } + + if (is_locked_coin(walletEx, (uint8_t (*)[32])(wtxid.u8), (uint32_t)i) && coinType != CoinType_OnlyMasternodeCollateral) { + continue; + } + + if ([account isSpent:outputValue]) { + continue; + } + + if (output.address == nil || ![account containsAddress:output.address]) { + continue; + } + + if (!allowUsedAddresses && [account transactionAddressAlreadySeenInOutputs:output.address]) { + continue; + } + + [vCoins addObject:[[DSInputCoin alloc] initWithTx:coin index:i]]; + + // Checks the sum amount of all UTXO's. + if (minimumSumAmount != MAX_MONEY) { + total += value; + + if (total >= minimumSumAmount) { + return vCoins; + } + } + + // Checks the maximum number of UTXO's. + if (maximumCount > 0 && vCoins.count >= maximumCount) { + return vCoins; + } + } } } - return vecTallyRet; -} - -void destroyInputValue(InputValue *value) { - DSLog(@"[OBJ-C] CoinJoin: 💀 InputValue"); - - if (value) { - free(value); - } + return vCoins; } -void destroyTransaction(Transaction *value) { - if (value) { - [DSTransaction ffi_free:value]; +- (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { + if (!is_denominated_amount(output.amount)) { + return false; } -} - -void destroySelectedCoins(SelectedCoins *selectedCoins) { - if (!selectedCoins) { - return; - } - - DSLog(@"[OBJ-C] CoinJoin: 💀 SelectedCoins"); - if (selectedCoins->item_count > 0 && selectedCoins->items) { - for (int i = 0; i < selectedCoins->item_count; i++) { - [DSCompactTallyItem ffi_free:selectedCoins->items[i]]; - } - - free(selectedCoins->items); + if (!is_fully_mixed(_wrapper.walletEx, (uint8_t (*)[32])(utxo.hash.u8), (uint32_t)utxo.n)) { + return false; } - free(selectedCoins); + return [self.chain.wallets.firstObject.accounts.firstObject.coinJoinDerivationPath containsAddress:output.address]; } -void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { - if (!gatheredOutputs) { - return; - } - - DSLog(@"[OBJ-C] CoinJoin: 💀 GatheredOutputs"); +-(uint64_t)getDenominatedBalance { + NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; - if (gatheredOutputs->item_count > 0 && gatheredOutputs->items) { - for (int i = 0; i < gatheredOutputs->item_count; i++) { - [DSTransactionOutput ffi_free:gatheredOutputs->items[i]->output]; - free(gatheredOutputs->items[i]->outpoint_hash); + DSUTXO outpoint; + NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; + for (NSValue *value in utxos) { + [value getValue:&outpoint]; + + if ([setWalletTxesCounted containsObject:uint256_data(outpoint.hash)]) { + continue; } - free(gatheredOutputs->items); + [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; + DSTransaction *wtx = [self.chain transactionForHash:outpoint.hash]; } - - free(gatheredOutputs); } -Transaction* signTransaction(Transaction *transaction, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: signTransaction"); +-(uint64_t)getAnonymizedBalance { - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; - destroy_transaction(transaction); + NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; + + DSUTXO outpoint; + NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; + for (NSValue *value in utxos) { + [value getValue:&outpoint]; + + if ([setWalletTxesCounted containsObject:uint256_data(outpoint.hash)]) { + continue; + } - BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx]; + [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; + DSTransaction *wtx = [self.chain transactionForHash:outpoint.hash]; - if (isSigned) { - return [tx ffi_malloc:wrapper.chain.chainType]; + if ([self isCoinJoinOutput:wtx.outputs[outpoint.n] utxo:outpoint]) { + } } - - return nil; -} - -unsigned int countInputsWithAmount(unsigned long long inputAmount, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: countInputsWithAmount"); - return [AS_OBJC(context) countInputsWithAmount:inputAmount]; -} - -ByteArray freshCoinJoinAddress(bool internal, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: freshCoinJoinAddress"); - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - NSString *address = [wrapper freshAddress:internal]; - - return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); } -bool commitTransaction(struct Recipient **items, uintptr_t item_count, const void *context) { - DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); - - NSMutableArray *amounts = [NSMutableArray array]; - NSMutableArray *scripts = [NSMutableArray array]; - - for (uintptr_t i = 0; i < item_count; i++) { - Recipient *recipient = items[i]; - [amounts addObject:@(recipient->amount)]; - NSData *script = [NSData dataWithBytes:recipient->script_pub_key.ptr length:recipient->script_pub_key.len]; - [scripts addObject:script]; - } - - // TODO: check subtract_fee_from_amount - bool result = false; +- (NSSet *)getSpendableTXs { + NSMutableSet *ret = [[NSMutableSet alloc] init]; + NSArray *unspent = self.chain.wallets.firstObject.unspentOutputs; - @synchronized (context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - result = [wrapper commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { - if (!error) { - // TODO: let balance_denominated_unconf = balance_info.denominated_untrusted_pending; - uint64_t balanceDenominatedUnconf = 0; - DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); - bool isFinished = finish_automatic_denominating(wrapper.manager.clientManager, balanceDenominatedUnconf, wrapper.balance_needs_anonymized); - DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); + DSUTXO outpoint; + for (uint32_t i = 0; i < unspent.count; i++) { + [unspent[i] getValue:&outpoint]; + DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; + + if (tx) { + [ret addObject:tx]; + + // Skip entries until we encounter a new TX + DSUTXO nextOutpoint; + while (i + 1 < unspent.count) { + [unspent[i + 1] getValue:&nextOutpoint]; + + if (!uint256_eq(nextOutpoint.hash, outpoint.hash)) { + break; + } + i++; } - }]; + } } - return result; + return ret; } -MasternodeEntry* masternodeByHash(uint8_t (*hash)[32], const void *context) { - UInt256 mnHash = *((UInt256 *)hash); - MasternodeEntry *masternode; +- (NSString *)freshAddress:(BOOL)internal { + NSString *address; + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; - @synchronized (context) { - masternode = [[AS_OBJC(context) masternodeEntryByHash:mnHash] ffi_malloc]; + if (internal) { + address = account.coinJoinChangeAddress; + DSLog(@"[OBJ-C] CoinJoin: freshChangeAddress, address: %@", address); + } else { + address = account.coinJoinReceiveAddress; + DSLog(@"[OBJ-C] CoinJoin: freshReceiveAddress, address: %@", address); } - return masternode; + return address; } -void destroyMasternodeEntry(MasternodeEntry *masternodeEntry) { - if (!masternodeEntry) { - return; - } +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished { + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES]; - DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeEntry"); - [DSSimplifiedMasternodeEntry ffi_free:masternodeEntry]; -} - -uint64_t validMNCount(const void *context) { - uint64_t result = 0; + if (!transaction) { + return NO; + } - @synchronized (context) { - result = [AS_OBJC(context) validMNCount]; + BOOL signedTransaction = [account signTransaction:transaction]; + + if (!signedTransaction || !transaction.isSigned) { + DSLog(@"[OBJ-C] CoinJoin error: not signed"); + return NO; + } else { + [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { + if (error) { + DSLog(@"[OBJ-C] CoinJoin publish error: %@", error.description); + onPublished(error); + } else { + DSLog(@"[OBJ-C] CoinJoin publish success: %@", transaction.description); + onPublished(nil); + } + }]; } - return result; + return YES; } -MasternodeList* getMNList(const void *context) { - MasternodeList *masternodes; - - @synchronized (context) { - masternodes = [[AS_OBJC(context) mnList] ffi_malloc]; - } - - return masternodes; +- (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash { + return [self.chainManager.masternodeManager.currentMasternodeList masternodeForRegistrationHash:hash]; } -void destroyMNList(MasternodeList *masternodeList) { // TODO: check destroyMasternodeList - if (!masternodeList) { - return; - } - - DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeList"); - [DSMasternodeList ffi_free:masternodeList]; +- (uint64_t)validMNCount { + return self.chainManager.masternodeManager.currentMasternodeList.validMasternodeCount; } -bool isBlockchainSynced(const void *context) { - BOOL result = NO; - - @synchronized (context) { - result = AS_OBJC(context).chainManager.combinedSyncProgress == 1.0; - } - - return result; +- (DSMasternodeList *)mnList { + return self.chainManager.masternodeManager.currentMasternodeList; } -bool isMasternodeOrDisconnectRequested(uint8_t (*ip_address)[16], uint16_t port, const void *context) { - BOOL result = NO; - - @synchronized (context) { - // TODO: ip_address, port - result = AS_OBJC(context).isMasternodeOrDisconnectRequested; - } - - return result; +- (BOOL)isMasternodeOrDisconnectRequested { + return [_masternodeGroup isMasternodeOrDisconnectRequested]; } -bool sendMessage(ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, const void *context) { - UInt128 ipAddress = *((UInt128 *)ip_address); - BOOL result = YES; // TODO - - @synchronized (context) { - NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; - [AS_OBJC(context) sendAcceptMessage:message withPeerIP:ipAddress port:port]; - } - - return result; +- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { + DSCoinJoinAcceptMessage *request = [[DSCoinJoinAcceptMessage alloc] initWithData:message]; + DSPeer *peer = [self.chainManager.peerManager peerForLocation:address port:port]; + [peer sendRequest:request]; +// [self.chainManager.peerManager sendRequest:request]; } @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 0f6b2f464..9ce87b711 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -16,39 +16,26 @@ // #import -#import "DSChain.h" -#import "DSTransactionOutput.h" -#import "DSCoinControl.h" -#import "DSCompactTallyItem.h" -#import "DSCoinJoinManager.h" #import "DSMasternodeGroup.h" +#import "DSChainManager.h" NS_ASSUME_NONNULL_BEGIN +@class DSCoinJoinManager; + @interface DSCoinJoinWrapper : NSObject -@property (nonatomic, assign, nullable) DSChainManager *chainManager; +@property (nonatomic, strong, nullable) DSChainManager *chainManager; +@property (nonatomic, strong) DSChain *chain; +@property (nonatomic, strong, nullable) DSCoinJoinManager *manager; @property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; + +@property (nonatomic, assign, nullable) WalletEx *walletEx; +@property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; -@property (nonatomic, assign) uint64_t balance_needs_anonymized; -@property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; -@property (nonatomic, assign) BOOL anonymizableTallyCached; -@property (nonatomic, strong) DSChain *chain; -@property (nonatomic, weak, nullable) DSCoinJoinManager *manager; - -- (instancetype)initWithManagers:(DSChainManager *)chainManager coinJoinManager: (DSCoinJoinManager*)coinJoinMnager; - -- (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; -- (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; -- (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; -- (uint32_t)countInputsWithAmount:(uint64_t)inputAmount; -- (NSString *)freshAddress:(BOOL)internal; -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished; -- (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; -- (uint64_t)validMNCount; -- (DSMasternodeList *)mnList; -- (BOOL)isMasternodeOrDisconnectRequested; -- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; + +- (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; +- (void)runCoinJoin; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 049c372db..ea9c052e1 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -15,407 +15,418 @@ // limitations under the License. // -#import "DSCoinJoinWrapper.h" -#import "DSTransaction.h" -#import "DSTransactionOutput.h" -#import "DSAccount.h" -#import "DSCoinControl.h" +#import "DSCoinJoinManager.h" #import "DSWallet.h" -#import "BigIntTypes.h" -#import "NSString+Bitcoin.h" +#import "DSTransaction+CoinJoin.h" +#import "DSTransactionOutput+CoinJoin.h" +#import "DSSimplifiedMasternodeEntry+Mndiff.h" +#import "DSMasternodeList+Mndiff.h" +#import "DSAccount.h" #import "DSChainManager.h" -#import "DSTransactionManager.h" -#import "DSMasternodeManager.h" -#import "DSCoinJoinAcceptMessage.h" -#import "DSPeerManager.h" +#import "DSCoinJoinWrapper.h" -int32_t const DEFAULT_MIN_DEPTH = 0; -int32_t const DEFAULT_MAX_DEPTH = 9999999; +#define AS_OBJC(context) ((__bridge DSCoinJoinWrapper *)(context)) +#define AS_RUST(context) ((__bridge void *)(context)) @implementation DSCoinJoinWrapper -- (instancetype)initWithManagers:(DSChainManager *)chainManager coinJoinManager: (DSCoinJoinManager*)coinJoinMnager { +- (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager { self = [super init]; if (self) { _chainManager = chainManager; - _manager = coinJoinMnager; + _manager = manager; + _masternodeGroup = [[DSMasternodeGroup alloc] init]; +// _wrapper.masternodeGroup = _masternodeGroup; } return self; } +- (void)runCoinJoin { + if (_options != NULL) { + free(_options); + _options = NULL; + } + + _options = [self createOptions]; + + DSLog(@"[OBJ-C] CoinJoin: register"); + _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage); + _clientManager = register_client_manager(AS_RUST(self), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); + + DSLog(@"[OBJ-C] CoinJoin: call"); + Balance *balance = [self getBalance]; + + run_client_manager(_clientManager, *balance); +// DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); +// free(balance); + + + // Might be useful: +// - (DSPeer *)peerForLocation:(UInt128)IPAddress port:(uint16_t)port + +// if ([self.masternodeManager hasMasternodeAtLocation:IPAddress port:port]) { +// return DSPeerType_MasterNode; +// } else { +// return DSPeerType_FullNode; +// } + +// - (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry + +// - (void)sendRequest:(DSMessageRequest *)request +} + +- (CoinJoinClientOptions *)createOptions { + CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); + options->enable_coinjoin = YES; + options->coinjoin_rounds = 1; + options->coinjoin_sessions = 1; + options->coinjoin_amount = DUFFS / 4; // 0.25 DASH + options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; + options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; + options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; + options->coinjoin_multi_session = NO; + DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.chainManager.chain.balance); + + return options; +} + +-(Balance *)getBalance { + Balance *balance = malloc(sizeof(Balance)); + balance->my_trusted = self.chainManager.chain.balance; + balance->denominated_trusted = [self.manager getDenominatedBalance]; + balance->anonymized = [self.manager getAnonymizedBalance]; + + balance->my_immature = 0; + balance->my_untrusted_pending = 0; + balance->denominated_untrusted_pending = 0; + balance->watch_only_trusted = 0; + balance->watch_only_untrusted_pending = 0; + balance->watch_only_immature = 0; + + return balance; +} + - (DSChain *)chain { return self.chainManager.chain; } -- (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { - DSTransaction *tx = [self.chain transactionForHash:txHash]; - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:tx]; +- (void)dealloc { + if (_options != NULL) { + free(_options); + } - if (index < tx.outputs.count) { - DSTransactionOutput *output = tx.outputs[index]; - - if ([account containsAddress:output.address]) { // TODO: is it the same as isPubKeyMine? - return YES; + unregister_client_manager(_clientManager); + unregister_wallet_ex(_walletEx); // Unregister last +} + +/// +/// MARK: Rust FFI callbacks +/// + +InputValue *getInputValueByPrevoutHash(uint8_t (*prevout_hash)[32], uint32_t index, const void *context) { + UInt256 txHash = *((UInt256 *)prevout_hash); + DSLog(@"[OBJ-C CALLBACK] CoinJoin: getInputValueByPrevoutHash"); + InputValue *inputValue = NULL; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + inputValue = malloc(sizeof(InputValue)); + DSWallet *wallet = wrapper.chain.wallets.firstObject; + int64_t value = [wallet inputValue:txHash inputIndex:index]; + + if (value != -1) { + inputValue->is_valid = TRUE; + inputValue->value = value; + } else { + inputValue->is_valid = FALSE; } } - return NO; + processor_destroy_block_hash(prevout_hash); + return inputValue; } -- (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress { + +bool hasChainLock(Block *block, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasChainLock"); + BOOL hasChainLock = NO; - @synchronized(self) { - // Note: cache is checked in dash-shared-core. - - uint64_t smallestDenom = coinjoin_get_smallest_denomination(); - NSMutableDictionary *mapTally = [[NSMutableDictionary alloc] init]; - NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; - - DSUTXO outpoint; - NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; - for (NSValue *value in utxos) { - [value getValue:&outpoint]; - - if ([setWalletTxesCounted containsObject:uint256_data(outpoint.hash)]) { - continue; - } - - [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; - DSTransaction *wtx = [self.chain transactionForHash:outpoint.hash]; - - if (wtx == nil) { - continue; - } - - if (wtx.isCoinbaseClassicTransaction && [wtx getBlocksToMaturity] > 0) { - continue; - } - - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:wtx]; - BOOL isTrusted = wtx.instantSendReceived || [account transactionIsVerified:wtx]; - - if (skipUnconfirmed && !isTrusted) { - continue; - } - - for (int32_t i = 0; i < wtx.outputs.count; i++) { - DSTransactionOutput *output = wtx.outputs[i]; - NSData *txDest = output.outScript; - NSString *address = [NSString bitcoinAddressWithScriptPubKey:txDest forChain:self.chain]; - - if (address == nil) { - continue; - } - - if (![account containsAddress:output.address]) { // TODO: is it the same as isPubKeyMine? - continue; - } - - DSCompactTallyItem *tallyItem = mapTally[txDest]; - - if (maxOupointsPerAddress != -1 && tallyItem != nil && tallyItem.inputCoins.count >= maxOupointsPerAddress) { - continue; - } - - if (is_locked_coin(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { - continue; - } - - if (skipDenominated && is_denominated_amount(output.amount)) { - continue; - } - - if (anonymizable) { - // ignore collaterals - if (is_collateral_amount(output.amount)) { - continue; - } - - // ignore outputs that are 10 times smaller then the smallest denomination - // otherwise they will just lead to higher fee / lower priority - if (output.amount <= smallestDenom/10) { - continue; - } - - // ignore mixed - if (is_fully_mixed(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { - continue; - } - } - - if (tallyItem == nil) { - tallyItem = [[DSCompactTallyItem alloc] init]; - tallyItem.txDestination = txDest; - mapTally[txDest] = tallyItem; - } - - tallyItem.amount += output.amount; - DSInputCoin *coin = [[DSInputCoin alloc] initWithTx:wtx index:i]; - [tallyItem.inputCoins addObject:coin]; - } + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + hasChainLock = [wrapper.chain blockHeightChainLocked:block->height]; + } + + processor_destroy_block(block); + return hasChainLock; +} + +Transaction *getTransaction(uint8_t (*tx_hash)[32], const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: getTransaction"); + UInt256 txHash = *((UInt256 *)tx_hash); + Transaction *tx = NULL; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + DSTransaction *transaction = [wrapper.chain transactionForHash:txHash]; + + if (transaction) { + tx = [transaction ffi_malloc:wrapper.chain.chainType]; } + } + + processor_destroy_block_hash(tx_hash); + return tx; +} - // construct resulting vector - // NOTE: vecTallyRet is "sorted" by txdest (i.e. address), just like mapTally - NSMutableArray *vecTallyRet = [[NSMutableArray alloc] init]; +bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: isMine"); + UInt256 txHash = *((UInt256 *)tx_hash); + BOOL result = NO; + + @synchronized (context) { + result = [AS_OBJC(context).manager isMineInput:txHash index:index]; + } + + processor_destroy_block_hash(tx_hash); + return result; +} + +GatheredOutputs* availableCoins(bool onlySafe, CoinControl coinControl, WalletEx *walletEx, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasCollateralInputs"); + GatheredOutputs *gatheredOutputs; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + ChainType chainType = wrapper.chain.chainType; + DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:&coinControl]; + NSArray *coins = [wrapper.manager availableCoins:walletEx onlySafe:onlySafe coinControl:cc minimumAmount:1 maximumAmount:MAX_MONEY minimumSumAmount:MAX_MONEY maximumCount:0]; - for (DSCompactTallyItem *item in mapTally.allValues) { - // TODO: (dashj) ignore this to get this dust back in - if (anonymizable && item.amount < smallestDenom) { - continue; - } - - [vecTallyRet addObject:item]; + gatheredOutputs = malloc(sizeof(GatheredOutputs)); + InputCoin **coinsArray = malloc(coins.count * sizeof(InputCoin *)); + + for (uintptr_t i = 0; i < coins.count; ++i) { + coinsArray[i] = [coins[i] ffi_malloc:chainType]; } - - // Note: cache is assigned in dash-shared-core - return vecTallyRet; + gatheredOutputs->items = coinsArray; + gatheredOutputs->item_count = (uintptr_t)coins.count; } + + return gatheredOutputs; } -- (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { - uint32_t total = 0; +SelectedCoins* selectCoinsGroupedByAddresses(bool skipDenominated, bool anonymizable, bool skipUnconfirmed, int maxOupointsPerAddress, WalletEx* walletEx, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: selectCoinsGroupedByAddresses"); + SelectedCoins *vecTallyRet; - @synchronized(self) { - NSArray *unspent = self.chain.wallets.firstObject.unspentOutputs; + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSArray *tempVecTallyRet = [wrapper.manager selectCoinsGroupedByAddresses:walletEx skipDenominated:skipDenominated anonymizable:anonymizable skipUnconfirmed:skipUnconfirmed maxOupointsPerAddress:maxOupointsPerAddress]; - DSUTXO outpoint; - for (uint32_t i = 0; i < unspent.count; i++) { - [unspent[i] getValue:&outpoint]; - DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; - - if (tx == NULL) { - continue; - } - - if (tx.outputs[outpoint.n].amount != inputAmount) { - continue; - } - - if (tx.confirmations < 0) { - continue; - } - - total++; + vecTallyRet = malloc(sizeof(SelectedCoins)); + vecTallyRet->item_count = tempVecTallyRet.count; + vecTallyRet->items = malloc(tempVecTallyRet.count * sizeof(CompactTallyItem *)); + + for (uint32_t i = 0; i < tempVecTallyRet.count; i++) { + vecTallyRet->items[i] = [tempVecTallyRet[i] ffi_malloc:wrapper.chain.chainType]; } } - return total; + return vecTallyRet; } -- (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount { - NSMutableArray *vCoins = [NSMutableArray array]; +void destroyInputValue(InputValue *value) { + DSLog(@"[OBJ-C] CoinJoin: 💀 InputValue"); - @synchronized(self) { - CoinType coinType = coinControl != nil ? coinControl.coinType : CoinType_AllCoins; + if (value) { + free(value); + } +} - uint64_t total = 0; - // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses - BOOL allowUsedAddresses = /* !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || */ (coinControl != nil && !coinControl.avoidAddressReuse); - int32_t minDepth = coinControl != nil ? coinControl.minDepth : DEFAULT_MIN_DEPTH; - int32_t maxDepth = coinControl != nil ? coinControl.maxDepth : DEFAULT_MAX_DEPTH; - - for (DSTransaction *coin in [self getSpendableTXs]) { - UInt256 wtxid = coin.txHash; - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:coin]; - - if ([account transactionIsPending:coin]) { - continue; - } - - if (coin.isImmatureCoinBase) { - continue; - } - - BOOL safeTx = coin.instantSendReceived || [account transactionIsVerified:coin]; - - if (onlySafe && !safeTx) { - continue; - } - - uint32_t depth = coin.confirmations; - - if (depth < minDepth || depth > maxDepth) { - continue; - } - - for (uint32_t i = 0; i < coin.outputs.count; i++) { - DSTransactionOutput *output = coin.outputs[i]; - DSLog(@"[OBJ-C] CoinJoin: check output: %llu", output.amount); - uint64_t value = output.amount; - BOOL found = NO; - - if (coinType == CoinType_OnlyFullyMixed) { - if (!is_denominated_amount(value)) { - continue; - } - - found = is_fully_mixed(walletEx, (uint8_t (*)[32])(wtxid.u8), (uint32_t)i); - } else if (coinType == CoinType_OnlyReadyToMix) { - if (!is_denominated_amount(value)) { - continue; - } - - found = !is_fully_mixed(walletEx, (uint8_t (*)[32])(wtxid.u8), (uint32_t)i); - } else if (coinType == CoinType_OnlyNonDenominated) { - if (is_collateral_amount(value)) { - continue; // do not use collateral amounts - } - - found = !is_denominated_amount(value); - } else if (coinType == CoinType_OnlyMasternodeCollateral) { - found = value == 1000 * DUFFS; - } else if (coinType == CoinType_OnlyCoinJoinCollateral) { - found = is_collateral_amount(value); - } else { - found = YES; - } - - if (!found) { - continue; - } - - if (value < minimumAmount || value > maximumAmount) { - continue; - } - - NSValue *outputValue = dsutxo_obj(((DSUTXO){wtxid, i})); - - if (coinControl != nil && coinControl.hasSelected && !coinControl.allowOtherInputs && ![coinControl isSelected:outputValue]) { - continue; - } - - if (is_locked_coin(walletEx, (uint8_t (*)[32])(wtxid.u8), (uint32_t)i) && coinType != CoinType_OnlyMasternodeCollateral) { - continue; - } - - if ([account isSpent:outputValue]) { - continue; - } - - if (output.address == nil || ![account containsAddress:output.address]) { - continue; - } - - if (!allowUsedAddresses && [account transactionAddressAlreadySeenInOutputs:output.address]) { - continue; - } - - [vCoins addObject:[[DSInputCoin alloc] initWithTx:coin index:i]]; - - // Checks the sum amount of all UTXO's. - if (minimumSumAmount != MAX_MONEY) { - total += value; - - if (total >= minimumSumAmount) { - return vCoins; - } - } - - // Checks the maximum number of UTXO's. - if (maximumCount > 0 && vCoins.count >= maximumCount) { - return vCoins; - } - } +void destroyTransaction(Transaction *value) { + if (value) { + [DSTransaction ffi_free:value]; + } +} + +void destroySelectedCoins(SelectedCoins *selectedCoins) { + if (!selectedCoins) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 SelectedCoins"); + + if (selectedCoins->item_count > 0 && selectedCoins->items) { + for (int i = 0; i < selectedCoins->item_count; i++) { + [DSCompactTallyItem ffi_free:selectedCoins->items[i]]; } + + free(selectedCoins->items); } - return vCoins; + free(selectedCoins); } -- (NSSet *)getSpendableTXs { - NSMutableSet *ret = [[NSMutableSet alloc] init]; - NSArray *unspent = self.chain.wallets.firstObject.unspentOutputs; +void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { + if (!gatheredOutputs) { + return; + } - DSUTXO outpoint; - for (uint32_t i = 0; i < unspent.count; i++) { - [unspent[i] getValue:&outpoint]; - DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; - - if (tx) { - [ret addObject:tx]; - - // Skip entries until we encounter a new TX - DSUTXO nextOutpoint; - while (i + 1 < unspent.count) { - [unspent[i + 1] getValue:&nextOutpoint]; - - if (!uint256_eq(nextOutpoint.hash, outpoint.hash)) { - break; - } - i++; - } + DSLog(@"[OBJ-C] CoinJoin: 💀 GatheredOutputs"); + + if (gatheredOutputs->item_count > 0 && gatheredOutputs->items) { + for (int i = 0; i < gatheredOutputs->item_count; i++) { + [DSTransactionOutput ffi_free:gatheredOutputs->items[i]->output]; + free(gatheredOutputs->items[i]->outpoint_hash); } + + free(gatheredOutputs->items); } - return ret; + free(gatheredOutputs); } -- (NSString *)freshAddress:(BOOL)internal { - NSString *address; - DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; +Transaction* signTransaction(Transaction *transaction, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: signTransaction"); - if (internal) { - address = account.coinJoinChangeAddress; - DSLog(@"[OBJ-C] CoinJoin: freshChangeAddress, address: %@", address); - } else { - address = account.coinJoinReceiveAddress; - DSLog(@"[OBJ-C] CoinJoin: freshReceiveAddress, address: %@", address); + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; + destroy_transaction(transaction); + + BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx]; + + if (isSigned) { + return [tx ffi_malloc:wrapper.chain.chainType]; + } } - return address; + return nil; } -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished { - DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; - DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES]; +unsigned int countInputsWithAmount(unsigned long long inputAmount, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: countInputsWithAmount"); + return [AS_OBJC(context).manager countInputsWithAmount:inputAmount]; +} + +ByteArray freshCoinJoinAddress(bool internal, const void *context) { + DSLog(@"[OBJ-C CALLBACK] CoinJoin: freshCoinJoinAddress"); + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSString *address = [wrapper.manager freshAddress:internal]; + + return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); +} + +bool commitTransaction(struct Recipient **items, uintptr_t item_count, const void *context) { + DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); + + NSMutableArray *amounts = [NSMutableArray array]; + NSMutableArray *scripts = [NSMutableArray array]; - if (!transaction) { - return NO; + for (uintptr_t i = 0; i < item_count; i++) { + Recipient *recipient = items[i]; + [amounts addObject:@(recipient->amount)]; + NSData *script = [NSData dataWithBytes:recipient->script_pub_key.ptr length:recipient->script_pub_key.len]; + [scripts addObject:script]; } - BOOL signedTransaction = [account signTransaction:transaction]; - - if (!signedTransaction || !transaction.isSigned) { - DSLog(@"[OBJ-C] CoinJoin error: not signed"); - return NO; - } else { - [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { - if (error) { - DSLog(@"[OBJ-C] CoinJoin publish error: %@", error.description); - onPublished(error); - } else { - DSLog(@"[OBJ-C] CoinJoin publish success: %@", transaction.description); - onPublished(nil); + // TODO: check subtract_fee_from_amount + bool result = false; + + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { + if (!error) { + DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); + bool isFinished = finish_automatic_denominating(wrapper.clientManager); + DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); } }]; } - return YES; + return result; +} + +MasternodeEntry* masternodeByHash(uint8_t (*hash)[32], const void *context) { + UInt256 mnHash = *((UInt256 *)hash); + MasternodeEntry *masternode; + + @synchronized (context) { + masternode = [[AS_OBJC(context).manager masternodeEntryByHash:mnHash] ffi_malloc]; + } + + return masternode; +} + +void destroyMasternodeEntry(MasternodeEntry *masternodeEntry) { + if (!masternodeEntry) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeEntry"); + [DSSimplifiedMasternodeEntry ffi_free:masternodeEntry]; +} + +uint64_t validMNCount(const void *context) { + uint64_t result = 0; + + @synchronized (context) { + result = [AS_OBJC(context).manager validMNCount]; + } + + return result; } -- (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash { - return [self.chainManager.masternodeManager.currentMasternodeList masternodeForRegistrationHash:hash]; +MasternodeList* getMNList(const void *context) { + MasternodeList *masternodes; + + @synchronized (context) { + masternodes = [[AS_OBJC(context).manager mnList] ffi_malloc]; + } + + return masternodes; } -- (uint64_t)validMNCount { - return self.chainManager.masternodeManager.currentMasternodeList.validMasternodeCount; +void destroyMNList(MasternodeList *masternodeList) { // TODO: check destroyMasternodeList + if (!masternodeList) { + return; + } + + DSLog(@"[OBJ-C] CoinJoin: 💀 MasternodeList"); + [DSMasternodeList ffi_free:masternodeList]; } -- (DSMasternodeList *)mnList { - return self.chainManager.masternodeManager.currentMasternodeList; +bool isBlockchainSynced(const void *context) { + BOOL result = NO; + + @synchronized (context) { + result = AS_OBJC(context).chainManager.combinedSyncProgress == 1.0; + } + + return result; } -- (BOOL)isMasternodeOrDisconnectRequested { - return [_masternodeGroup isMasternodeOrDisconnectRequested]; +bool isMasternodeOrDisconnectRequested(uint8_t (*ip_address)[16], uint16_t port, const void *context) { + BOOL result = NO; + + @synchronized (context) { + // TODO: ip_address, port + result = AS_OBJC(context).manager.isMasternodeOrDisconnectRequested; + } + + return result; } -- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { - DSCoinJoinAcceptMessage *request = [[DSCoinJoinAcceptMessage alloc] initWithData:message]; - DSPeer *peer = [self.chainManager.peerManager peerForLocation:address port:port]; - [peer sendRequest:request]; -// [self.chainManager.peerManager sendRequest:request]; +bool sendMessage(ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, const void *context) { + UInt128 ipAddress = *((UInt128 *)ip_address); + BOOL result = YES; // TODO + + @synchronized (context) { + NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; + [AS_OBJC(context).manager sendAcceptMessage:message withPeerIP:ipAddress port:port]; + } + + return result; } @end From 6daf3f92c460ae576ff156983705ef7ac6cd7ba4 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 28 Apr 2024 18:16:44 +0800 Subject: [PATCH 016/133] fix: recalculate balance --- .../Models/CoinJoin/DSCoinJoinManager.h | 8 +-- .../Models/CoinJoin/DSCoinJoinManager.m | 61 +++++++++++-------- .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 36 ++++------- 4 files changed, 52 insertions(+), 55 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 0234c816c..df4fb0fd5 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -34,9 +34,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong) DSChain *chain; -@property (nonatomic, weak, nullable) DSCoinJoinWrapper *wrapper; +@property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; -- (instancetype)initWithWrapper:(DSCoinJoinWrapper *)coinJoinWrapper chainManager:(DSChainManager *)chainManager; +- (instancetype)initWithChainManager:(DSChainManager *)chainManager; - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; - (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; @@ -49,8 +49,8 @@ NS_ASSUME_NONNULL_BEGIN - (DSMasternodeList *)mnList; - (BOOL)isMasternodeOrDisconnectRequested; - (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; -- (uint64_t)getDenominatedBalance; -- (uint64_t)getAnonymizedBalance; +- (Balance *)getBalance; +- (void)runCoinJoin; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 2dadfec3e..6a04d9117 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -34,11 +34,11 @@ @implementation DSCoinJoinManager -- (instancetype)initWithWrapper:(DSCoinJoinWrapper *)coinJoinWrapper chainManager:(DSChainManager *)chainManager { +- (instancetype)initWithChainManager:(DSChainManager *)chainManager { self = [super init]; if (self) { _chainManager = chainManager; - _wrapper = coinJoinWrapper; + _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chainManager]; } return self; } @@ -47,6 +47,10 @@ - (DSChain *)chain { return self.chainManager.chain; } +- (void)runCoinJoin { + [_wrapper runCoinJoin]; +} + - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { DSTransaction *tx = [self.chain transactionForHash:txHash]; DSAccount *account = [self.chain firstAccountThatCanContainTransaction:tx]; @@ -242,7 +246,7 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { for (uint32_t i = 0; i < coin.outputs.count; i++) { DSTransactionOutput *output = coin.outputs[i]; - DSLog(@"[OBJ-C] CoinJoin: check output: %llu", output.amount); +// DSLog(@"[OBJ-C] CoinJoin: check output: %llu", output.amount); uint64_t value = output.amount; BOOL found = NO; @@ -336,26 +340,10 @@ - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { return [self.chain.wallets.firstObject.accounts.firstObject.coinJoinDerivationPath containsAddress:output.address]; } --(uint64_t)getDenominatedBalance { - NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; - - DSUTXO outpoint; - NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; - for (NSValue *value in utxos) { - [value getValue:&outpoint]; - - if ([setWalletTxesCounted containsObject:uint256_data(outpoint.hash)]) { - continue; - } - - [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; - DSTransaction *wtx = [self.chain transactionForHash:outpoint.hash]; - } -} - --(uint64_t)getAnonymizedBalance { - +- (Balance *)getBalance { NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; + uint64_t anonymizedBalance = 0; + uint64_t denominatedBalance = 0; DSUTXO outpoint; NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; @@ -367,12 +355,36 @@ -(uint64_t)getAnonymizedBalance { } [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; - DSTransaction *wtx = [self.chain transactionForHash:outpoint.hash]; + DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; - if ([self isCoinJoinOutput:wtx.outputs[outpoint.n] utxo:outpoint]) { + for (int32_t i = 0; i < tx.outputs.count; i++) { + DSTransactionOutput *output = tx.outputs[i]; + if ([self isCoinJoinOutput:output utxo:outpoint]) { + anonymizedBalance += output.amount; + } + + if (is_denominated_amount(output.amount)) { + denominatedBalance += output.amount; + } } } + + DSLog(@"[OBJ-C] CoinJoin: denominatedBalance: %llu", denominatedBalance); + + Balance *balance = malloc(sizeof(Balance)); + balance->my_trusted = self.chainManager.chain.balance; + balance->denominated_trusted = denominatedBalance; + balance->anonymized = anonymizedBalance; + + balance->my_immature = 0; + balance->my_untrusted_pending = 0; + balance->denominated_untrusted_pending = 0; + balance->watch_only_trusted = 0; + balance->watch_only_untrusted_pending = 0; + balance->watch_only_immature = 0; + + return balance; } - (NSSet *)getSpendableTXs { @@ -466,7 +478,6 @@ - (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(ui DSCoinJoinAcceptMessage *request = [[DSCoinJoinAcceptMessage alloc] initWithData:message]; DSPeer *peer = [self.chainManager.peerManager peerForLocation:address port:port]; [peer sendRequest:request]; -// [self.chainManager.peerManager sendRequest:request]; } @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 9ce87b711..f8052353a 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) DSChainManager *chainManager; @property (nonatomic, strong) DSChain *chain; -@property (nonatomic, strong, nullable) DSCoinJoinManager *manager; +@property (nonatomic, weak, nullable) DSCoinJoinManager *manager; @property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; @property (nonatomic, assign, nullable) WalletEx *walletEx; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index ea9c052e1..90f3f1a79 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -42,23 +42,25 @@ - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSCh } - (void)runCoinJoin { - if (_options != NULL) { - free(_options); - _options = NULL; + if (_options == NULL) { + _options = [self createOptions]; } - _options = [self createOptions]; + if (_walletEx == NULL) { + DSLog(@"[OBJ-C] CoinJoin: register"); + _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage); + } - DSLog(@"[OBJ-C] CoinJoin: register"); - _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage); - _clientManager = register_client_manager(AS_RUST(self), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); + if (_clientManager == NULL) { + _clientManager = register_client_manager(AS_RUST(self), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); + } DSLog(@"[OBJ-C] CoinJoin: call"); - Balance *balance = [self getBalance]; + Balance *balance = [self.manager getBalance]; run_client_manager(_clientManager, *balance); // DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); -// free(balance); + free(balance); // Might be useful: @@ -90,22 +92,6 @@ - (CoinJoinClientOptions *)createOptions { return options; } --(Balance *)getBalance { - Balance *balance = malloc(sizeof(Balance)); - balance->my_trusted = self.chainManager.chain.balance; - balance->denominated_trusted = [self.manager getDenominatedBalance]; - balance->anonymized = [self.manager getAnonymizedBalance]; - - balance->my_immature = 0; - balance->my_untrusted_pending = 0; - balance->denominated_untrusted_pending = 0; - balance->watch_only_trusted = 0; - balance->watch_only_untrusted_pending = 0; - balance->watch_only_immature = 0; - - return balance; -} - - (DSChain *)chain { return self.chainManager.chain; } From b5025f5027c14ed42d036de60dd8f79cdc55626b Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sat, 4 May 2024 19:46:04 +0800 Subject: [PATCH 017/133] fix: get script from an actuall output if empty in initWithTransaction --- .../shared/Models/CoinJoin/DSTransaction+CoinJoin.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m index ca8e610d7..df3d1a673 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -36,7 +36,15 @@ - (DSTransaction *)initWithTransaction:(Transaction *)transaction onChain:(DSCha UInt256 hashValue; memcpy(hashValue.u8, *input->input_hash, 32); NSNumber *index = @(input->index); - NSData *script = [NSData dataWithBytes:input->script length:input->script_length]; + NSData *script; + + if (input->script && input->script_length != 0) { + script = [NSData dataWithBytes:input->script length:input->script_length]; + } else { + DSTransaction *inputTx = [chain transactionForHash:hashValue]; + script = [inputTx.outputs objectAtIndex:index.integerValue].outScript; + } + NSNumber *sequence = @(input->sequence); [hashes addObject:uint256_obj(hashValue)]; From 46cd8b74731ef0afd826ec23c7d49960fb06c3b1 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 7 May 2024 19:31:29 +0700 Subject: [PATCH 018/133] feat: sending DSA to a connected masternode --- DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h | 1 + DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m | 3 +-- DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m | 6 +++++- .../shared/Models/Managers/Chain Managers/DSPeerManager.h | 2 ++ .../shared/Models/Managers/Chain Managers/DSPeerManager.m | 4 ++++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index df4fb0fd5..73761e6b0 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -49,6 +49,7 @@ NS_ASSUME_NONNULL_BEGIN - (DSMasternodeList *)mnList; - (BOOL)isMasternodeOrDisconnectRequested; - (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; +- (void)connectToMasternodeWithIP:(UInt128)address port:(uint16_t)port; - (Balance *)getBalance; - (void)runCoinJoin; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 6a04d9117..e22eccaa3 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -246,7 +246,6 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { for (uint32_t i = 0; i < coin.outputs.count; i++) { DSTransactionOutput *output = coin.outputs[i]; -// DSLog(@"[OBJ-C] CoinJoin: check output: %llu", output.amount); uint64_t value = output.amount; BOOL found = NO; @@ -476,7 +475,7 @@ - (BOOL)isMasternodeOrDisconnectRequested { - (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { DSCoinJoinAcceptMessage *request = [[DSCoinJoinAcceptMessage alloc] initWithData:message]; - DSPeer *peer = [self.chainManager.peerManager peerForLocation:address port:port]; + DSPeer *peer = [self.chainManager.peerManager connectedPeer]; // TODO: coinjoin peer management [peer sendRequest:request]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 90f3f1a79..d0b131b60 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -367,7 +367,11 @@ uint64_t validMNCount(const void *context) { MasternodeList *masternodes; @synchronized (context) { - masternodes = [[AS_OBJC(context).manager mnList] ffi_malloc]; + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + DSMasternodeList *mnList = [wrapper.manager mnList]; + // TODO: might have 0 valid MNs, account for this + DSLog(@"[OBJ-C] CoinJoin: getMNList, valid count: %llu", mnList.validMasternodeCount); + masternodes = [mnList ffi_malloc]; } return masternodes; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h index 9f6938521..6bd09c757 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h @@ -92,6 +92,8 @@ typedef NS_ENUM(uint16_t, DSDisconnectReason) - (void)sendRequest:(DSMessageRequest *)request; +- (DSPeer *)connectedPeer; // TODO(coinjoin): temp + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index 7a1ef376f..19907841d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -169,6 +169,10 @@ - (NSSet *)misbehavingPeers { } } +- (DSPeer *)connectedPeer { // TODO(coinjoin): temp + return self.connectedPeers.objectEnumerator.nextObject; +} + // MARK: - Managers - (DSMasternodeManager *)masternodeManager { From 49507d99b786db554482ba532476495a97995cb8 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 13 May 2024 18:55:20 +0700 Subject: [PATCH 019/133] feat: senddsq & MasternodeGroup draft --- .../Models/CoinJoin/DSCoinJoinManager.h | 7 +- .../Models/CoinJoin/DSCoinJoinManager.m | 31 +++++- .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 21 +++- .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 18 +++- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 98 ++++++++++++++++++- .../Managers/Chain Managers/DSPeerManager.h | 4 + .../Managers/Chain Managers/DSPeerManager.m | 24 ++++- .../CoinJoin/DSCoinJoinAcceptMessage.h | 2 +- .../CoinJoin/DSCoinJoinAcceptMessage.m | 4 + .../Messages/CoinJoin/DSSendCoinJoinQueue.h | 30 ++++++ .../Messages/CoinJoin/DSSendCoinJoinQueue.m | 39 ++++++++ Example/DashSync/DSCoinJoinViewController.m | 1 + Example/Podfile.lock | 4 +- 14 files changed, 270 insertions(+), 15 deletions(-) create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.h create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.m diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 73761e6b0..b8d365889 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -35,6 +35,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong) DSChain *chain; @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; +@property (nonatomic, readonly) BOOL isWaitingForNewBlock; +@property (nonatomic, readonly) BOOL isMixing; - (instancetype)initWithChainManager:(DSChainManager *)chainManager; @@ -49,9 +51,12 @@ NS_ASSUME_NONNULL_BEGIN - (DSMasternodeList *)mnList; - (BOOL)isMasternodeOrDisconnectRequested; - (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; -- (void)connectToMasternodeWithIP:(UInt128)address port:(uint16_t)port; - (Balance *)getBalance; + +- (void)startAsync; +- (void)stopAsync; - (void)runCoinJoin; +- (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index e22eccaa3..c299bfad0 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -39,6 +39,7 @@ - (instancetype)initWithChainManager:(DSChainManager *)chainManager { if (self) { _chainManager = chainManager; _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chainManager]; + _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; } return self; } @@ -47,6 +48,22 @@ - (DSChain *)chain { return self.chainManager.chain; } +- (void)startAsync { + if (!_masternodeGroup.isRunning) { + DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); + [self.chainManager.peerManager shouldSendDsq:true]; + [_masternodeGroup startAsync]; + } +} + +- (void)stopAsync { + if (_masternodeGroup != nil && _masternodeGroup.isRunning) { + [self.chainManager.peerManager shouldSendDsq:false]; + [_masternodeGroup stopAsync]; + _masternodeGroup = nil; + } +} + - (void)runCoinJoin { [_wrapper runCoinJoin]; } @@ -474,9 +491,21 @@ - (BOOL)isMasternodeOrDisconnectRequested { } - (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { - DSCoinJoinAcceptMessage *request = [[DSCoinJoinAcceptMessage alloc] initWithData:message]; + DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; DSPeer *peer = [self.chainManager.peerManager connectedPeer]; // TODO: coinjoin peer management [peer sendRequest:request]; } +- (BOOL)isWaitingForNewBlock { + return [self.wrapper isWaitingForNewBlock]; +} + +- (BOOL)isMixing { + return false; +} + +- (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { + return [_masternodeGroup addPendingMasternode:proTxHash clientSessionId:sessionId]; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index f8052353a..df361b5c7 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -28,7 +28,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) DSChainManager *chainManager; @property (nonatomic, strong) DSChain *chain; @property (nonatomic, weak, nullable) DSCoinJoinManager *manager; -@property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; @property (nonatomic, assign, nullable) WalletEx *walletEx; @property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; @@ -36,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; - (void)runCoinJoin; +- (BOOL)isWaitingForNewBlock; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index d0b131b60..674e93441 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -35,8 +35,6 @@ - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSCh if (self) { _chainManager = chainManager; _manager = manager; - _masternodeGroup = [[DSMasternodeGroup alloc] init]; -// _wrapper.masternodeGroup = _masternodeGroup; } return self; } @@ -48,7 +46,7 @@ - (void)runCoinJoin { if (_walletEx == NULL) { DSLog(@"[OBJ-C] CoinJoin: register"); - _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage); + _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage, addPendingMasternode); } if (_clientManager == NULL) { @@ -77,6 +75,10 @@ - (void)runCoinJoin { // - (void)sendRequest:(DSMessageRequest *)request } +- (BOOL)isWaitingForNewBlock { + return is_waiting_for_new_block(_clientManager); +} + - (CoinJoinClientOptions *)createOptions { CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); options->enable_coinjoin = YES; @@ -413,10 +415,23 @@ bool sendMessage(ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, @synchronized (context) { NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; + // TODO: different messages support [AS_OBJC(context).manager sendAcceptMessage:message withPeerIP:ipAddress port:port]; } return result; } +bool addPendingMasternode(uint8_t (*pro_tx_hash)[32], uint8_t (*session_id)[32], const void *context) { + UInt256 sessionId = *((UInt256 *)session_id); + UInt256 proTxHash = *((UInt256 *)pro_tx_hash); + BOOL result = NO; + + @synchronized (context) { + result = [AS_OBJC(context).manager addPendingMasternode:proTxHash clientSessionId:sessionId]; + } + + return result; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index d21fce17a..0c410c00b 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -16,12 +16,28 @@ // #import +#import "DSChain.h" NS_ASSUME_NONNULL_BEGIN +@class DSCoinJoinManager; + @interface DSMasternodeGroup : NSObject --(BOOL)isMasternodeOrDisconnectRequested; +@property (atomic, readonly) BOOL isRunning; +@property (nonatomic, strong) id blocksObserver; +@property (nonatomic, strong) DSChain *chain; +@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; +@property (nonatomic, strong) NSMutableSet *pendingSessions; +@property (nonatomic, strong) NSMutableDictionary *masternodeMap; +@property (atomic, readonly) NSUInteger maxConnections; + +- (instancetype)initWithManager:(DSCoinJoinManager *)manager; + +- (void)startAsync; +- (void)stopAsync; +- (BOOL)isMasternodeOrDisconnectRequested; +- (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index a6e26764e..8b0ff8b0a 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -16,12 +16,108 @@ // #import "DSMasternodeGroup.h" +#import "DSChainManager.h" +#import "DSChain+Protected.h" +#import "DSCoinJoinManager.h" + +uint64_t const MIN_PEER_DISCOVERY_INTERVAL = 1000; @implementation DSMasternodeGroup --(BOOL)isMasternodeOrDisconnectRequested { +- (instancetype)initWithManager:(DSCoinJoinManager *)manager { + self = [super init]; + if (self) { + _coinJoinManager = manager; + _pendingSessions = [NSMutableSet set]; + _masternodeMap = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)startAsync { + _isRunning = true; + self.blocksObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification + object:nil + queue:nil + usingBlock:^(NSNotification *note) { + if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) { + + DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); + [self triggerConnections]; + } + }]; +} + +- (dispatch_queue_t)networkingQueue { + return self.chain.networkingQueue; +} + +- (void)stopAsync { + _isRunning = false; + [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; +} + +- (BOOL)isMasternodeOrDisconnectRequested { // TODO: return NO; } +- (void)triggerConnections { + dispatch_async(self.networkingQueue, ^{ + if (!self.isRunning) { + return; + } + + if (self.coinJoinManager.isWaitingForNewBlock) { + + } + }); +} + +- (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { + @synchronized (self) { + DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@", (unsigned long)self.maxConnections, uint256_hex(proTxHash)); + NSValue *sessionIdValue = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; + [_pendingSessions addObject:sessionIdValue]; + + NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; + [_masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; + + [self updateMaxConnections]; + [self checkMasternodesWithoutSessions]; + } + + return true; +} + +- (void)updateMaxConnections { + _maxConnections = _pendingSessions.count; + NSUInteger connections = MIN(self.maxConnections, DEFAULT_COINJOIN_SESSIONS); + DSLog(@"[OBJ-C] CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); + + [self updateMaxConnections:connections]; +} + +- (void)updateMaxConnections:(NSUInteger)connections { + _maxConnections = connections; + + if (!self.isRunning) { + return; + } + + // TODO: + // We may now have too many or too few open connections. Add more or drop some to get to the right amount. +// adjustment = maxConnections - channels.getConnectedClientCount(); +// if (adjustment > 0) +// triggerConnections(); +// +// if (adjustment < 0) +// channels.closeConnections(-adjustment); +} + +- (void)checkMasternodesWithoutSessions { + // TODO: +} + @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h index 6bd09c757..92ed589be 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h @@ -75,6 +75,7 @@ typedef NS_ENUM(uint16_t, DSDisconnectReason) @property (nonatomic, readonly) NSArray *registeredDevnetPeers; @property (nonatomic, readonly) NSArray *registeredDevnetPeerServices; @property (nullable, nonatomic, readonly) NSString *trustedPeerHost; +@property (nonatomic, readonly) BOOL shouldSendDsq; - (DSPeer *)peerForLocation:(UInt128)IPAddress port:(uint16_t)port; - (DSPeerStatus)statusForLocation:(UInt128)IPAddress port:(uint32_t)port; @@ -92,7 +93,10 @@ typedef NS_ENUM(uint16_t, DSDisconnectReason) - (void)sendRequest:(DSMessageRequest *)request; +// MARK: CoinJoin + - (DSPeer *)connectedPeer; // TODO(coinjoin): temp +- (void)shouldSendDsq:(BOOL)shouldSendDsq; @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index 19907841d..48e4b0a9d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -55,6 +55,7 @@ #import "NSError+Dash.h" #import "NSManagedObject+Sugar.h" #import "NSString+Bitcoin.h" +#import "DSSendCoinJoinQueue.h" #import #import @@ -169,10 +170,6 @@ - (NSSet *)misbehavingPeers { } } -- (DSPeer *)connectedPeer { // TODO(coinjoin): temp - return self.connectedPeers.objectEnumerator.nextObject; -} - // MARK: - Managers - (DSMasternodeManager *)masternodeManager { @@ -916,6 +913,11 @@ - (void)peerConnected:(DSPeer *)peer { if (!self.masternodeList) { [peer sendGetaddrMessage]; // request a list of other dash peers } + + if (self.shouldSendDsq) { + [peer sendRequest:[DSSendCoinJoinQueue requestWithShouldSend:true]]; + } + dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; }); @@ -1084,4 +1086,18 @@ - (void)sendRequest:(DSMessageRequest *)request { [self.downloadPeer sendRequest:request]; } +// MARK: CoinJoin + +- (DSPeer *)connectedPeer { // TODO(coinjoin): temp + return self.connectedPeers.objectEnumerator.nextObject; +} + +- (void)shouldSendDsq:(BOOL)shouldSendDsq { + for (DSPeer *peer in self.connectedPeers) { + DSSendCoinJoinQueue *request = [DSSendCoinJoinQueue requestWithShouldSend:shouldSendDsq]; + [peer sendRequest:request]; + } + _shouldSendDsq = shouldSendDsq; +} + @end diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h index 808318959..f8ec40fee 100644 --- a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h @@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSData *data; -- (instancetype)initWithData:(NSData *)data; ++ (instancetype)requestWithData:(NSData *)data; @end diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m index 117e1cf73..dfa32c643 100644 --- a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m @@ -21,6 +21,10 @@ @implementation DSCoinJoinAcceptMessage ++ (instancetype)requestWithData:(NSData *)data { + return [[DSCoinJoinAcceptMessage alloc] initWithData:data]; +} + - (instancetype)initWithData:(NSData *)data { self = [super init]; if (self) { diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.h b/DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.h new file mode 100644 index 000000000..af37f22e3 --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.h @@ -0,0 +1,30 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSMessageRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSSendCoinJoinQueue : DSMessageRequest + +@property (nonatomic, readonly) BOOL send; + ++ (instancetype)requestWithShouldSend:(BOOL)shouldSend; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.m b/DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.m new file mode 100644 index 000000000..02a09fe32 --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSSendCoinJoinQueue.m @@ -0,0 +1,39 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSSendCoinJoinQueue.h" +#import "DSPeer.h" +#import "NSData+Dash.h" + +@implementation DSSendCoinJoinQueue + ++ (instancetype)requestWithShouldSend:(BOOL)shouldSend { + return [[DSSendCoinJoinQueue alloc] initWithShouldSend:shouldSend]; +} + +- (instancetype)initWithShouldSend:(BOOL)shouldSend { + self = [super initWithType:MSG_SENDDSQ]; + if (self) { + _send = shouldSend; + } + return self; +} + +- (NSData *)toData { + return [NSData dataWithUInt8:(uint8_t)_send]; +} +@end diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index b151bed90..6920823a2 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -49,6 +49,7 @@ - (void)startCoinJoin { _coinJoinManager = [[DSCoinJoinManager alloc] initWithChainManager:self.chainManager]; } + [_coinJoinManager startAsync]; [_coinJoinManager runCoinJoin]; } diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 2455f529c..a38f52ec8 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -656,7 +656,7 @@ PODS: - gRPC/Interface-Legacy (1.49.0): - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) - - Protobuf (3.25.3) + - Protobuf (3.26.1) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -722,7 +722,7 @@ SPEC CHECKSUMS: gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: 8e9074797a13c484a79959fdb819ef4ae6da7dbe + Protobuf: a53f5173a603075b3522a5c50be63a67a5f3353a SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 From a934f5c187e1b95a45866ea1765dbb2f35cdf7a2 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 4 Jun 2024 16:49:32 +0700 Subject: [PATCH 020/133] feat: different message types support & disconnect logic for masternodegroup --- .../Models/CoinJoin/DSCoinJoinManager.h | 5 +- .../Models/CoinJoin/DSCoinJoinManager.m | 25 ++++-- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 49 +++++----- .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 6 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 89 ++++++++++++++++++- .../CoinJoin/DSCoinJoinAcceptMessage.h | 1 + .../CoinJoin/DSCoinJoinAcceptMessage.m | 6 +- .../CoinJoin/DSCoinJoinEntryMessage.h | 32 +++++++ .../CoinJoin/DSCoinJoinEntryMessage.m | 46 ++++++++++ DashSync/shared/Models/Network/DSPeer.h | 3 +- DashSync/shared/Models/Network/DSPeer.m | 5 -- 12 files changed, 227 insertions(+), 41 deletions(-) create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.h create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.m diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index b8d365889..73519c9fc 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -49,8 +49,9 @@ NS_ASSUME_NONNULL_BEGIN - (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; - (uint64_t)validMNCount; - (DSMasternodeList *)mnList; -- (BOOL)isMasternodeOrDisconnectRequested; -- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; +- (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port; +- (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; +- (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; - (Balance *)getBalance; - (void)startAsync; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index c299bfad0..04edf933b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -486,14 +486,29 @@ - (DSMasternodeList *)mnList { return self.chainManager.masternodeManager.currentMasternodeList; } -- (BOOL)isMasternodeOrDisconnectRequested { - return [_masternodeGroup isMasternodeOrDisconnectRequested]; +- (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { + return [_masternodeGroup isMasternodeOrDisconnectRequested:ip port:port]; } -- (void)sendAcceptMessage:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { - DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; +- (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { + return [_masternodeGroup disconnectMasternode:ip port:port]; +} + +- (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { DSPeer *peer = [self.chainManager.peerManager connectedPeer]; // TODO: coinjoin peer management - [peer sendRequest:request]; + + if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { + DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; + [peer sendRequest:request]; + } else if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { + DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; + [peer sendRequest:request]; + } else { + DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); + return NO; + } + + return YES; } - (BOOL)isWaitingForNewBlock { diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index df361b5c7..264779b91 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, nullable) WalletEx *walletEx; @property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; +@property (nonatomic, assign, nullable) CoinJoinClientQueueManager *clientQueueManager; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 674e93441..e877d0959 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -45,34 +45,26 @@ - (void)runCoinJoin { } if (_walletEx == NULL) { - DSLog(@"[OBJ-C] CoinJoin: register"); - _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, sendMessage, addPendingMasternode); + DSLog(@"[OBJ-C] CoinJoin: register wallet ex"); + _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode); } if (_clientManager == NULL) { + DSLog(@"[OBJ-C] CoinJoin: register client manager"); _clientManager = register_client_manager(AS_RUST(self), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); } + if (_clientQueueManager == NULL) { + DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); + _clientQueueManager = register_client_queue_manager(_clientManager, _options, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, AS_RUST(self)); + } + DSLog(@"[OBJ-C] CoinJoin: call"); Balance *balance = [self.manager getBalance]; - run_client_manager(_clientManager, *balance); + run_client_manager(_clientManager, _clientQueueManager, *balance); // DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); free(balance); - - - // Might be useful: -// - (DSPeer *)peerForLocation:(UInt128)IPAddress port:(uint16_t)port - -// if ([self.masternodeManager hasMasternodeAtLocation:IPAddress port:port]) { -// return DSPeerType_MasterNode; -// } else { -// return DSPeerType_FullNode; -// } - -// - (instancetype)initWithSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry - -// - (void)sendRequest:(DSMessageRequest *)request } - (BOOL)isWaitingForNewBlock { @@ -399,24 +391,35 @@ bool isBlockchainSynced(const void *context) { } bool isMasternodeOrDisconnectRequested(uint8_t (*ip_address)[16], uint16_t port, const void *context) { + UInt128 ipAddress = *((UInt128 *)ip_address); + BOOL result = NO; + + @synchronized (context) { + result = [AS_OBJC(context).manager isMasternodeOrDisconnectRequested:ipAddress port:port]; + } + + return result; +} + +bool disconnectMasternode(uint8_t (*ip_address)[16], uint16_t port, const void *context) { + UInt128 ipAddress = *((UInt128 *)ip_address); BOOL result = NO; @synchronized (context) { - // TODO: ip_address, port - result = AS_OBJC(context).manager.isMasternodeOrDisconnectRequested; + result = [AS_OBJC(context).manager disconnectMasternode:ipAddress port:port]; } return result; } -bool sendMessage(ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, const void *context) { +bool sendMessage(char *message_type, ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, const void *context) { + NSString *messageType = [NSString stringWithUTF8String:message_type]; UInt128 ipAddress = *((UInt128 *)ip_address); - BOOL result = YES; // TODO + BOOL result = YES; @synchronized (context) { NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; - // TODO: different messages support - [AS_OBJC(context).manager sendAcceptMessage:message withPeerIP:ipAddress port:port]; + result = [AS_OBJC(context).manager sendMessageOfType:messageType message:message withPeerIP:ipAddress port:port]; } return result; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index 0c410c00b..e999b5aad 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -31,14 +31,18 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) NSMutableSet *pendingSessions; @property (nonatomic, strong) NSMutableDictionary *masternodeMap; @property (atomic, readonly) NSUInteger maxConnections; +@property (nonatomic, strong) NSMutableArray *pendingClosingMasternodes; +@property (nonatomic, strong) NSLock *lock; - (instancetype)initWithManager:(DSCoinJoinManager *)manager; - (void)startAsync; - (void)stopAsync; -- (BOOL)isMasternodeOrDisconnectRequested; +- (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port; +- (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 8b0ff8b0a..0203ec548 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -19,6 +19,8 @@ #import "DSChainManager.h" #import "DSChain+Protected.h" #import "DSCoinJoinManager.h" +#import "DSSimplifiedMasternodeEntry.h" +#import uint64_t const MIN_PEER_DISCOVERY_INTERVAL = 1000; @@ -58,11 +60,83 @@ - (void)stopAsync { [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; } -- (BOOL)isMasternodeOrDisconnectRequested { - // TODO: +- (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { + BOOL found = [self forPeer:ip port:port warn:NO withPredicate:^BOOL(DSPeer *peer) { + return YES; + }]; + + if (!found) { + for (DSPeer *mn in self.pendingClosingMasternodes) { + if (uint128_eq(mn.address, ip) && mn.port == port) { + found = true; + } + } + } + + return found; +} + +- (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { + return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { + DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); + + [self.lock lock]; + [self.pendingClosingMasternodes addObject:peer]; + // TODO (dashj): what if this disconnects the wrong one + [self updateMaxConnections]; + [self.lock unlock]; + [peer disconnect]; + + return true; + }]; +} + +- (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate { + NSArray *peerList = [self getConnectedPeers]; + NSMutableString *listOfPeers = [NSMutableString string]; + + for (DSPeer *peer in peerList) { + [listOfPeers appendFormat:@"%@, ", peer.location]; + + if (uint128_eq(peer.address, ip) && peer.port == port) { + return predicate(peer); + } + } + + if (warn) { + if (![self isNodePending:ip port:port]) { + DSLog(@"[OBJ-C] CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); + NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); + } else { + DSLog(@"[OBJ-C] CoinJoin: %@ in the list of pending peers: %@", [self hostFor:ip], listOfPeers); + } + } + return NO; } +- (BOOL)isNodePending:(UInt128)ip port:(uint16_t)port { + NSArray *pendingPeers = [self getConnectedPeers]; + + for (DSPeer *peer in pendingPeers) { + if (uint128_eq(peer.address, ip) && peer.port == port) { + return true; + } + } + + return false; +} + +- (NSArray *)getConnectedPeers { + // TODO: + return @[]; +} + +- (NSArray *)getPendingPeers { + // TODO: + return @[]; +} + - (void)triggerConnections { dispatch_async(self.networkingQueue, ^{ if (!self.isRunning) { @@ -70,7 +144,7 @@ - (void)triggerConnections { } if (self.coinJoinManager.isWaitingForNewBlock) { - + // TODO } }); } @@ -120,4 +194,13 @@ - (void)checkMasternodesWithoutSessions { // TODO: } +- (NSString *)hostFor:(UInt128)address { + char s[INET6_ADDRSTRLEN]; + + if (address.u64[0] == 0 && address.u32[2] == CFSwapInt32HostToBig(0xffff)) { + return @(inet_ntop(AF_INET, &address.u32[3], s, sizeof(s))); + } else + return @(inet_ntop(AF_INET6, &address, s, sizeof(s))); +} + @end diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h index f8ec40fee..61ae08762 100644 --- a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.h @@ -25,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) NSData *data; + (instancetype)requestWithData:(NSData *)data; ++ (NSString *)type; @end diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m index dfa32c643..f58f2f30b 100644 --- a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinAcceptMessage.m @@ -25,6 +25,10 @@ + (instancetype)requestWithData:(NSData *)data { return [[DSCoinJoinAcceptMessage alloc] initWithData:data]; } ++ (NSString *)type { + return MSG_COINJOIN_ACCEPT; +} + - (instancetype)initWithData:(NSData *)data { self = [super init]; if (self) { @@ -34,7 +38,7 @@ - (instancetype)initWithData:(NSData *)data { } - (NSString *)type { - return MSG_COINJOIN_ACCEPT; + return DSCoinJoinAcceptMessage.type; } - (NSData *)toData { diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.h b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.h new file mode 100644 index 000000000..883ef01c1 --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.h @@ -0,0 +1,32 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSMessageRequest.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DSCoinJoinEntryMessage : DSMessageRequest + +@property (nonatomic, readonly) NSData *data; + ++ (instancetype)requestWithData:(NSData *)data; ++ (NSString *)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.m b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.m new file mode 100644 index 000000000..a5394b25d --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinEntryMessage.m @@ -0,0 +1,46 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSCoinJoinEntryMessage.h" +#import "DSPeer.h" + +@implementation DSCoinJoinEntryMessage + ++ (instancetype)requestWithData:(NSData *)data { + return [[DSCoinJoinEntryMessage alloc] initWithData:data]; +} + ++ (NSString *)type { + return MSG_COINJOIN_ENTRY; +} + +- (instancetype)initWithData:(NSData *)data { + self = [super init]; + if (self) { + _data = data; + } + return self; +} + +- (NSString *)type { + return DSCoinJoinEntryMessage.type; +} + +- (NSData *)toData { + return self.data; +} +@end diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index 845cf2841..5c7092081 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -140,9 +140,10 @@ typedef NS_ENUM(uint32_t, DSInvType) // CoinJoin #define MSG_COINJOIN_ACCEPT @"dsa" +#define MSG_COINJOIN_ENTRY @"dsi" + #define MSG_DARKSENDCONTROL @"dsc" #define MSG_DARKSENDFINISH @"dsf" -#define MSG_DARKSENDINITIATE @"dsi" #define MSG_DARKSENDQUORUM @"dsq" #define MSG_DARKSENDSESSION @"dss" #define MSG_DARKSENDSESSIONUPDATE @"dssu" diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 4c5988ee9..76e7456ae 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -847,8 +847,6 @@ - (void)acceptMessage:(NSData *)message type:(NSString *)type { [self acceptDarksendControlMessage:message]; else if ([MSG_DARKSENDFINISH isEqual:type]) [self acceptDarksendFinishMessage:message]; - else if ([MSG_DARKSENDINITIATE isEqual:type]) - [self acceptDarksendInitiateMessage:message]; else if ([MSG_DARKSENDQUORUM isEqual:type]) [self acceptDarksendQuorumMessage:message]; else if ([MSG_DARKSENDSESSION isEqual:type]) @@ -1829,9 +1827,6 @@ - (void)acceptDarksendFinishMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsf"); } -- (void)acceptDarksendInitiateMessage:(NSData *)message { -} - - (void)acceptDarksendQuorumMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsq"); } From 25f0f9311995d40297a65e3efd0cc837263b1dc0 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 10 Jun 2024 16:42:42 +0700 Subject: [PATCH 021/133] feat: process dsq --- .../Models/CoinJoin/DSCoinJoinManager.h | 7 ++- .../Models/CoinJoin/DSCoinJoinManager.m | 59 +++++++++++++++---- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 29 ++++++--- DashSync/shared/Models/Network/DSPeer.h | 2 +- DashSync/shared/Models/Network/DSPeer.m | 16 +++-- Example/DashSync/DSCoinJoinViewController.m | 2 +- 7 files changed, 85 insertions(+), 31 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 73519c9fc..987917494 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -28,17 +28,17 @@ NS_ASSUME_NONNULL_BEGIN @interface DSCoinJoinManager : NSObject -@property (nonatomic, assign, nullable) DSChainManager *chainManager; +@property (nonatomic, assign, nullable) DSChain *chain; @property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; -@property (nonatomic, strong) DSChain *chain; @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @property (nonatomic, readonly) BOOL isWaitingForNewBlock; @property (nonatomic, readonly) BOOL isMixing; -- (instancetype)initWithChainManager:(DSChainManager *)chainManager; ++ (instancetype)sharedInstanceForChain:(DSChain *)chain; +- (instancetype)initWithChain:(DSChain *)chain; - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; - (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; @@ -58,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)stopAsync; - (void)runCoinJoin; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; +- (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 04edf933b..24cc9a937 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -28,37 +28,56 @@ #import "DSMasternodeManager.h" #import "DSCoinJoinAcceptMessage.h" #import "DSPeerManager.h" +#import "DSSimplifiedMasternodeEntry.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @implementation DSCoinJoinManager -- (instancetype)initWithChainManager:(DSChainManager *)chainManager { +static NSMutableDictionary *_managerChainDictionary = nil; +static dispatch_once_t managerChainToken = 0; + ++ (instancetype)sharedInstanceForChain:(DSChain *)chain { + NSParameterAssert(chain); + + dispatch_once(&managerChainToken, ^{ + _managerChainDictionary = [NSMutableDictionary dictionary]; + }); + DSCoinJoinManager *managerForChain = nil; + @synchronized(self) { + if (![_managerChainDictionary objectForKey:chain.uniqueID]) { + managerForChain = [[DSCoinJoinManager alloc] initWithChain:chain]; + _managerChainDictionary[chain.uniqueID] = managerForChain; + } else { + managerForChain = [_managerChainDictionary objectForKey:chain.uniqueID]; + } + } + return managerForChain; +} + +- (instancetype)initWithChain:(DSChain *)chain { self = [super init]; if (self) { - _chainManager = chainManager; - _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chainManager]; + _chain = chain; + _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chain.chainManager]; _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; } return self; } -- (DSChain *)chain { - return self.chainManager.chain; -} - (void)startAsync { if (!_masternodeGroup.isRunning) { DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); - [self.chainManager.peerManager shouldSendDsq:true]; + [self.chain.chainManager.peerManager shouldSendDsq:true]; [_masternodeGroup startAsync]; } } - (void)stopAsync { if (_masternodeGroup != nil && _masternodeGroup.isRunning) { - [self.chainManager.peerManager shouldSendDsq:false]; + [self.chain.chainManager.peerManager shouldSendDsq:false]; [_masternodeGroup stopAsync]; _masternodeGroup = nil; } @@ -68,6 +87,20 @@ - (void)runCoinJoin { [_wrapper runCoinJoin]; } +- (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { + if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { + [_wrapper processDSQueueFrom:peer message:message]; + } +// else if ([message isKindOfClass:[CoinJoinBroadcastTx class]]) { +// [self processBroadcastTx:(CoinJoinBroadcastTx *)message]; +// } else { +// for (CoinJoinClientManager *clientManager in coinJoinClientManagers.allValues) { +// [clientManager processMessageFromPeer:from message:message flag:NO]; +// } +// } +} + + - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { DSTransaction *tx = [self.chain transactionForHash:txHash]; DSAccount *account = [self.chain firstAccountThatCanContainTransaction:tx]; @@ -389,7 +422,7 @@ - (Balance *)getBalance { DSLog(@"[OBJ-C] CoinJoin: denominatedBalance: %llu", denominatedBalance); Balance *balance = malloc(sizeof(Balance)); - balance->my_trusted = self.chainManager.chain.balance; + balance->my_trusted = self.chain.chainManager.chain.balance; balance->denominated_trusted = denominatedBalance; balance->anonymized = anonymizedBalance; @@ -475,15 +508,15 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output } - (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash { - return [self.chainManager.masternodeManager.currentMasternodeList masternodeForRegistrationHash:hash]; + return [self.chain.chainManager.masternodeManager.currentMasternodeList masternodeForRegistrationHash:uint256_reverse(hash)]; } - (uint64_t)validMNCount { - return self.chainManager.masternodeManager.currentMasternodeList.validMasternodeCount; + return self.chain.chainManager.masternodeManager.currentMasternodeList.validMasternodeCount; } - (DSMasternodeList *)mnList { - return self.chainManager.masternodeManager.currentMasternodeList; + return self.chain.chainManager.masternodeManager.currentMasternodeList; } - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { @@ -495,7 +528,7 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { } - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { - DSPeer *peer = [self.chainManager.peerManager connectedPeer]; // TODO: coinjoin peer management + DSPeer *peer = [self.chain.chainManager.peerManager connectedPeer]; // TODO: coinjoin peer management if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 264779b91..2579fa955 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; - (void)runCoinJoin; - (BOOL)isWaitingForNewBlock; +- (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index e877d0959..2fe2f39e7 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -58,13 +58,6 @@ - (void)runCoinJoin { DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); _clientQueueManager = register_client_queue_manager(_clientManager, _options, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, AS_RUST(self)); } - - DSLog(@"[OBJ-C] CoinJoin: call"); - Balance *balance = [self.manager getBalance]; - - run_client_manager(_clientManager, _clientQueueManager, *balance); -// DSLog(@"[OBJ-C] CoinJoin: do_automatic_denominating result: %llu", self.wrapper.balance_needs_anonymized); - free(balance); } - (BOOL)isWaitingForNewBlock { @@ -86,6 +79,28 @@ - (CoinJoinClientOptions *)createOptions { return options; } +- (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { + ByteArray *array = malloc(sizeof(ByteArray)); + array->len = (uintptr_t)message.length; + array->ptr = data_malloc(message); + + process_ds_queue(_clientQueueManager, peer.address.u8, peer.port, array); + + if (array) { + if (array->ptr) { + free((void *)array->ptr); + } + + free(array); + } + + DSLog(@"[OBJ-C] CoinJoin: call"); + Balance *balance = [self.manager getBalance]; + + run_client_manager(_clientManager, _clientQueueManager, *balance); + free(balance); +} + - (DSChain *)chain { return self.chainManager.chain; } diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index 5c7092081..8917ca200 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -141,10 +141,10 @@ typedef NS_ENUM(uint32_t, DSInvType) #define MSG_COINJOIN_ACCEPT @"dsa" #define MSG_COINJOIN_ENTRY @"dsi" +#define MSG_COINJOIN_QUEUE @"dsq" #define MSG_DARKSENDCONTROL @"dsc" #define MSG_DARKSENDFINISH @"dsf" -#define MSG_DARKSENDQUORUM @"dsq" #define MSG_DARKSENDSESSION @"dss" #define MSG_DARKSENDSESSIONUPDATE @"dssu" #define MSG_DARKSENDTX @"dstx" diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 76e7456ae..709f4ee55 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -63,6 +63,7 @@ #import "DSTransactionHashEntity+CoreDataClass.h" #import "DSTransactionInvRequest.h" #import "DSVersionRequest.h" +#import "DSCoinJoinManager.h" #import "NSData+DSHash.h" #import "NSData+Dash.h" #import "NSDate+Utils.h" @@ -211,7 +212,10 @@ - (NSString *)location { } - (NSString *)debugLocation { - return [NSString stringWithFormat:@"[%@: %@]", self.chain.name, self.location]; + DSChain *chain = self.chain; + NSString *host = self.host; + DSLog(@"%@", host); + return [NSString stringWithFormat:@"[%@: %@]", chain, self.location]; } - (NSString *)host { @@ -842,13 +846,13 @@ - (void)acceptMessage:(NSData *)message type:(NSString *)type { [self acceptGovObjectMessage:message]; //else if ([MSG_GOVOBJSYNC isEqual:type]) [self acceptGovObjectSyncMessage:message]; - //private send + // CoinJoin if ([MSG_DARKSENDCONTROL isEqual:type]) [self acceptDarksendControlMessage:message]; else if ([MSG_DARKSENDFINISH isEqual:type]) [self acceptDarksendFinishMessage:message]; - else if ([MSG_DARKSENDQUORUM isEqual:type]) - [self acceptDarksendQuorumMessage:message]; + else if ([MSG_COINJOIN_QUEUE isEqual:type]) + [self acceptCoinJoinQueueMessage:message]; else if ([MSG_DARKSENDSESSION isEqual:type]) [self acceptDarksendSessionMessage:message]; else if ([MSG_DARKSENDSESSIONUPDATE isEqual:type]) @@ -1827,8 +1831,8 @@ - (void)acceptDarksendFinishMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsf"); } -- (void)acceptDarksendQuorumMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsq"); +- (void)acceptCoinJoinQueueMessage:(NSData *)message { + [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } - (void)acceptDarksendSessionMessage:(NSData *)message { diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index 6920823a2..f9c1e7e1f 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -46,7 +46,7 @@ - (void)startCoinJoin { // TODO: refreshUnusedKeys() if (_coinJoinManager == NULL) { - _coinJoinManager = [[DSCoinJoinManager alloc] initWithChainManager:self.chainManager]; + _coinJoinManager = [DSCoinJoinManager sharedInstanceForChain:_chainManager.chain]; } [_coinJoinManager startAsync]; From 90a183e24d64dd58a0870f2189a90cf493705e14 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 10 Jun 2024 16:56:13 +0700 Subject: [PATCH 022/133] chore: cleanup --- DashSync/shared/Models/Network/DSPeer.m | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 709f4ee55..898af5870 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -212,10 +212,7 @@ - (NSString *)location { } - (NSString *)debugLocation { - DSChain *chain = self.chain; - NSString *host = self.host; - DSLog(@"%@", host); - return [NSString stringWithFormat:@"[%@: %@]", chain, self.location]; + return [NSString stringWithFormat:@"[%@: %@]", self.chain.name, self.location]; } - (NSString *)host { From 29fe732a5293ae1fb4e1a03f19bd08822fcf8b78 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 13 Jun 2024 19:08:54 +0700 Subject: [PATCH 023/133] fix: concurrent borrow issues --- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 - .../Models/CoinJoin/DSCoinJoinWrapper.m | 40 +++++++++---------- .../Models/CoinJoin/DSCompactTallyItem.m | 1 - DashSync/shared/Models/CoinJoin/DSInputCoin.m | 1 - 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 2579fa955..638b0cea8 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -31,7 +31,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, nullable) WalletEx *walletEx; @property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; -@property (nonatomic, assign, nullable) CoinJoinClientQueueManager *clientQueueManager; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 2fe2f39e7..64db0744a 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -52,11 +52,9 @@ - (void)runCoinJoin { if (_clientManager == NULL) { DSLog(@"[OBJ-C] CoinJoin: register client manager"); _clientManager = register_client_manager(AS_RUST(self), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); - } - - if (_clientQueueManager == NULL) { + DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); - _clientQueueManager = register_client_queue_manager(_clientManager, _options, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, AS_RUST(self)); + add_client_queue_manager(_clientManager, _options, masternodeByHash, destroyMasternodeEntry, validMNCount, isBlockchainSynced, AS_RUST(self)); } } @@ -80,25 +78,27 @@ - (CoinJoinClientOptions *)createOptions { } - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { - ByteArray *array = malloc(sizeof(ByteArray)); - array->len = (uintptr_t)message.length; - array->ptr = data_malloc(message); - - process_ds_queue(_clientQueueManager, peer.address.u8, peer.port, array); - - if (array) { - if (array->ptr) { - free((void *)array->ptr); + @synchronized (self) { + ByteArray *array = malloc(sizeof(ByteArray)); + array->len = (uintptr_t)message.length; + array->ptr = data_malloc(message); + + process_ds_queue(_clientManager, peer.address.u8, peer.port, array); + + if (array) { + if (array->ptr) { + free((void *)array->ptr); + } + + free(array); } + + DSLog(@"[OBJ-C] CoinJoin: call"); + Balance *balance = [self.manager getBalance]; - free(array); + run_client_manager(_clientManager, *balance); + free(balance); } - - DSLog(@"[OBJ-C] CoinJoin: call"); - Balance *balance = [self.manager getBalance]; - - run_client_manager(_clientManager, _clientQueueManager, *balance); - free(balance); } - (DSChain *)chain { diff --git a/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m b/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m index a2d778804..fb8b8f28a 100644 --- a/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m +++ b/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m @@ -64,7 +64,6 @@ + (void)ffi_free:(CompactTallyItem *)item { } free(item); - DSLog(@"[OBJ-C] CoinJoin: 💀 CompactTallyItem"); } @end diff --git a/DashSync/shared/Models/CoinJoin/DSInputCoin.m b/DashSync/shared/Models/CoinJoin/DSInputCoin.m index fcb5d6308..10a95d6d7 100644 --- a/DashSync/shared/Models/CoinJoin/DSInputCoin.m +++ b/DashSync/shared/Models/CoinJoin/DSInputCoin.m @@ -53,7 +53,6 @@ + (void)ffi_free:(InputCoin *)inputCoin { } free(inputCoin); - DSLog(@"[OBJ-C] CoinJoin: 💀 InputCoin"); } @end From caa81f86bec020063371ea0a01beb0e701fc0dcc Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 18 Jun 2024 12:29:47 +0700 Subject: [PATCH 024/133] feat: process dssu & dsc, masternode group logic --- .../Models/CoinJoin/DSCoinJoinManager.h | 1 + .../Models/CoinJoin/DSCoinJoinManager.m | 43 +++---- .../Models/CoinJoin/DSCoinJoinWrapper.h | 3 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 28 ++++- .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 5 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 105 +++++++++++++++--- .../Chain Managers/DSMasternodeManager.h | 1 + .../Chain Managers/DSMasternodeManager.m | 8 +- DashSync/shared/Models/Network/DSPeer.h | 6 +- DashSync/shared/Models/Network/DSPeer.m | 21 ++-- 10 files changed, 169 insertions(+), 52 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 987917494..1abedaaa9 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -59,6 +59,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)runCoinJoin; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; +- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 24cc9a937..43c06c937 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -90,14 +90,11 @@ - (void)runCoinJoin { - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { [_wrapper processDSQueueFrom:peer message:message]; + } else if ([type isEqualToString:MSG_COINJOIN_BROADCAST_TX]) { +// [_wrapper processBroadcastTxFrom:peer message:message]; + } else { + [_wrapper processMessageFrom:peer message:message type:type]; } -// else if ([message isKindOfClass:[CoinJoinBroadcastTx class]]) { -// [self processBroadcastTx:(CoinJoinBroadcastTx *)message]; -// } else { -// for (CoinJoinClientManager *clientManager in coinJoinClientManagers.allValues) { -// [clientManager processMessageFromPeer:from message:message flag:NO]; -// } -// } } @@ -528,20 +525,20 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { } - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { - DSPeer *peer = [self.chain.chainManager.peerManager connectedPeer]; // TODO: coinjoin peer management - - if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { - DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; - [peer sendRequest:request]; - } else if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { - DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; - [peer sendRequest:request]; - } else { - DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); - return NO; - } + return [self.masternodeGroup forPeer:address port:port warn:true withPredicate:^BOOL(DSPeer * _Nonnull peer) { + if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { + DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; + [peer sendRequest:request]; + } else if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { + DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; + [peer sendRequest:request]; + } else { + DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); + return NO; + } - return YES; + return YES; + }]; } - (BOOL)isWaitingForNewBlock { @@ -549,11 +546,15 @@ - (BOOL)isWaitingForNewBlock { } - (BOOL)isMixing { - return false; + return [self.wrapper isMixing]; } - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { return [_masternodeGroup addPendingMasternode:proTxHash clientSessionId:sessionId]; } +- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId { + return [_wrapper mixingMasternodeAddressFor:clientSessionId]; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 638b0cea8..e7722a0b5 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -36,7 +36,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; - (void)runCoinJoin; - (BOOL)isWaitingForNewBlock; +- (BOOL)isMixing; - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message; +- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; +- (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 64db0744a..3bea768d1 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -62,6 +62,10 @@ - (BOOL)isWaitingForNewBlock { return is_waiting_for_new_block(_clientManager); } +- (BOOL)isMixing { + return is_mixing(_clientManager); +} + - (CoinJoinClientOptions *)createOptions { CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); options->enable_coinjoin = YES; @@ -92,7 +96,7 @@ - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { free(array); } - + DSLog(@"[OBJ-C] CoinJoin: call"); Balance *balance = [self.manager getBalance]; @@ -101,6 +105,28 @@ - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { } } +- (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { + @synchronized (self) { + ByteArray *array = malloc(sizeof(ByteArray)); + array->len = (uintptr_t)message.length; + array->ptr = data_malloc(message); + + process_coinjoin_message(_clientManager, peer.address.u8, peer.port, array, [type UTF8String]); + + if (array) { + if (array->ptr) { + free((void *)array->ptr); + } + + free(array); + } + } +} + +- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId { + return mixing_masternode_address(_clientManager, (uint8_t (*)[32])(clientSessionId.u8)); +} + - (DSChain *)chain { return self.chainManager.chain; } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index e999b5aad..6eed389fb 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -30,9 +30,12 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; @property (nonatomic, strong) NSMutableSet *pendingSessions; @property (nonatomic, strong) NSMutableDictionary *masternodeMap; +@property (nonatomic, strong) NSMutableDictionary *addressMap; @property (atomic, readonly) NSUInteger maxConnections; @property (nonatomic, strong) NSMutableArray *pendingClosingMasternodes; @property (nonatomic, strong) NSLock *lock; +@property (nonatomic, strong) NSMutableSet *mutableConnectedPeers; +@property (nonatomic, strong) NSMutableSet *mutablePendingPeers; - (instancetype)initWithManager:(DSCoinJoinManager *)manager; @@ -41,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port; - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; - +- (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate; @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 0203ec548..bfb987587 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -20,6 +20,8 @@ #import "DSChain+Protected.h" #import "DSCoinJoinManager.h" #import "DSSimplifiedMasternodeEntry.h" +#import "DSMasternodeManager.h" +#import "DSPeerManager.h" #import uint64_t const MIN_PEER_DISCOVERY_INTERVAL = 1000; @@ -32,6 +34,8 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _coinJoinManager = manager; _pendingSessions = [NSMutableSet set]; _masternodeMap = [NSMutableDictionary dictionary]; + _mutableConnectedPeers = [NSMutableSet set]; + _mutablePendingPeers = [NSMutableSet set]; } return self; } @@ -92,10 +96,11 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { } - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate { - NSArray *peerList = [self getConnectedPeers]; + return predicate([_chain.chainManager.peerManager connectedPeer]); // TODO: finish peer management + NSMutableString *listOfPeers = [NSMutableString string]; - for (DSPeer *peer in peerList) { + for (DSPeer *peer in self.connectedPeers) { [listOfPeers appendFormat:@"%@, ", peer.location]; if (uint128_eq(peer.address, ip) && peer.port == port) { @@ -116,9 +121,7 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B } - (BOOL)isNodePending:(UInt128)ip port:(uint16_t)port { - NSArray *pendingPeers = [self getConnectedPeers]; - - for (DSPeer *peer in pendingPeers) { + for (DSPeer *peer in self.pendingPeers) { if (uint128_eq(peer.address, ip) && peer.port == port) { return true; } @@ -127,14 +130,16 @@ - (BOOL)isNodePending:(UInt128)ip port:(uint16_t)port { return false; } -- (NSArray *)getConnectedPeers { - // TODO: - return @[]; +- (NSSet *)connectedPeers { + @synchronized(self.mutableConnectedPeers) { + return [self.mutableConnectedPeers copy]; + } } -- (NSArray *)getPendingPeers { - // TODO: - return @[]; +- (NSSet *)pendingPeers { + @synchronized(self.mutablePendingPeers) { + return [self.mutablePendingPeers copy]; + } } - (void)triggerConnections { @@ -143,16 +148,46 @@ - (void)triggerConnections { return; } - if (self.coinJoinManager.isWaitingForNewBlock) { - // TODO + if (self.coinJoinManager.isWaitingForNewBlock || self.coinJoinManager.isMixing) { + return; } + + }); } +- (NSArray *)getPeers { + NSMutableArray *addresses = [NSMutableArray array]; + + @synchronized(_addressMap) { + for (NSValue *sessionValue in _pendingSessions) { + UInt256 sessionId; + [sessionValue getValue:&sessionId]; + SocketAddress *mixingMasternodeInfo = [_coinJoinManager mixingMasternodeAddressFor:sessionId]; + + if (mixingMasternodeInfo) { + UInt128 ipAddress = *((UInt128 *)mixingMasternodeInfo->ip_address); + uint16_t port = mixingMasternodeInfo->port; + DSPeer *peer = [_chain.chainManager.peerManager peerForLocation:ipAddress port:port]; + + if (![_pendingClosingMasternodes containsObject:peer]) { + [addresses addObject:peer]; + [_addressMap setObject:sessionValue forKey:peer.location]; + DSLog(@"[OBJ-C] CoinJoin: discovery: %@ -> %@", peer.location, uint256_hex(sessionId)); + } + + destroy_socket_address(mixingMasternodeInfo); + } + } + } + + return [addresses copy]; +} + - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self) { DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@", (unsigned long)self.maxConnections, uint256_hex(proTxHash)); - NSValue *sessionIdValue = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; + NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [_pendingSessions addObject:sessionIdValue]; NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; @@ -191,7 +226,47 @@ - (void)updateMaxConnections:(NSUInteger)connections { } - (void)checkMasternodesWithoutSessions { - // TODO: + NSMutableArray *masternodesToDrop = [NSMutableArray array]; + + @synchronized (_pendingSessions) { + for (DSPeer *peer in self.connectedPeers) { + BOOL found = false; + + for (NSValue *value in _pendingSessions) { + UInt256 sessionId; + [value getValue:&sessionId]; + SocketAddress *mixingMasternodeAddress = [_coinJoinManager mixingMasternodeAddressFor:sessionId]; + + if (mixingMasternodeAddress) { + UInt128 ipAddress = *((UInt128 *)mixingMasternodeAddress->ip_address); + uint16_t port = mixingMasternodeAddress->port; + + if (uint128_eq(ipAddress, peer.address) && port == peer.port) { + found = YES; + } + + destroy_socket_address(mixingMasternodeAddress); + } else { + // TODO(DashJ): we may not need this anymore + DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); + } + } + + if (!found) { + DSLog(@"[OBJ-C] CoinJoin: masternode is not connected to a session: %@", peer.location); + [masternodesToDrop addObject:peer]; + } + } + } + + DSLog(@"[OBJ-C] CoinJoin: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); + + for (DSPeer *peer in masternodesToDrop) { + DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; + //pendingSessions.remove(mn.getProTxHash()); TODO: recheck (commented in DashJ) + DSLog(@"[OBJ-C] CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + [peer disconnect]; + } } - (NSString *)hostFor:(UInt128)address { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h index 0af7fe899..c8b0403b5 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h @@ -70,6 +70,7 @@ NS_ASSUME_NONNULL_BEGIN - (DSSimplifiedMasternodeEntry *)masternodeHavingProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash; +- (DSSimplifiedMasternodeEntry *)masternodeAtLocation:(UInt128)IPAddress port:(uint32_t)port; - (BOOL)hasMasternodeAtLocation:(UInt128)IPAddress port:(uint32_t)port; - (DSQuorumEntry *_Nullable)quorumEntryForInstantSendRequestID:(UInt256)requestID withBlockHeightOffset:(uint32_t)blockHeightOffset; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m index dca4860ca..4bb035161 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m @@ -156,12 +156,16 @@ - (NSUInteger)activeQuorumsCount { } - (BOOL)hasMasternodeAtLocation:(UInt128)IPAddress port:(uint32_t)port { + return [self masternodeAtLocation:IPAddress port:port] != nil; +} + +- (DSSimplifiedMasternodeEntry *)masternodeAtLocation:(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 simplifiedMasternodeEntry; } } - return NO; + return nil; } - (NSUInteger)masternodeListRetrievalQueueCount { diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index 8917ca200..190cf726f 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -142,12 +142,12 @@ typedef NS_ENUM(uint32_t, DSInvType) #define MSG_COINJOIN_ACCEPT @"dsa" #define MSG_COINJOIN_ENTRY @"dsi" #define MSG_COINJOIN_QUEUE @"dsq" +#define MSG_COINJOIN_BROADCAST_TX @"dstx" +#define MSG_COINJOIN_STATUS_UPDATE @"dssu" +#define MSG_COINJOIN_COMPLETE @"dsc" -#define MSG_DARKSENDCONTROL @"dsc" #define MSG_DARKSENDFINISH @"dsf" #define MSG_DARKSENDSESSION @"dss" -#define MSG_DARKSENDSESSIONUPDATE @"dssu" -#define MSG_DARKSENDTX @"dstx" #define REJECT_INVALID 0x10 // transaction is invalid for some reason (invalid signature, output value > input, etc) #define REJECT_SPENT 0x12 // an input is already spent diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 898af5870..4678ed3f0 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -844,18 +844,18 @@ - (void)acceptMessage:(NSData *)message type:(NSString *)type { //else if ([MSG_GOVOBJSYNC isEqual:type]) [self acceptGovObjectSyncMessage:message]; // CoinJoin - if ([MSG_DARKSENDCONTROL isEqual:type]) - [self acceptDarksendControlMessage:message]; + else if ([MSG_COINJOIN_COMPLETE isEqual:type]) + [self acceptCoinJoinCompleteMessage:message]; else if ([MSG_DARKSENDFINISH isEqual:type]) [self acceptDarksendFinishMessage:message]; else if ([MSG_COINJOIN_QUEUE isEqual:type]) [self acceptCoinJoinQueueMessage:message]; else if ([MSG_DARKSENDSESSION isEqual:type]) [self acceptDarksendSessionMessage:message]; - else if ([MSG_DARKSENDSESSIONUPDATE isEqual:type]) - [self acceptDarksendSessionUpdateMessage:message]; - else if ([MSG_DARKSENDTX isEqual:type]) - [self acceptDarksendTransactionMessage:message]; + else if ([MSG_COINJOIN_STATUS_UPDATE isEqual:type]) + [self acceptCoinJoinStatusUpdateMessage:message]; + else if ([MSG_COINJOIN_BROADCAST_TX isEqual:type]) + [self acceptCoinJoinBroadcastTxMessage:message]; #if DROP_MESSAGE_LOGGING else { DSLogWithLocation(self, @"dropping %@, len:%u, not implemented", type, message.length); @@ -1820,8 +1820,9 @@ - (void)acceptGovObjectSyncMessage:(NSData *)message { - (void)acceptDarksendAnnounceMessage:(NSData *)message { } -- (void)acceptDarksendControlMessage:(NSData *)message { +- (void)acceptCoinJoinCompleteMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsc"); + [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_COMPLETE]; } - (void)acceptDarksendFinishMessage:(NSData *)message { @@ -1829,17 +1830,19 @@ - (void)acceptDarksendFinishMessage:(NSData *)message { } - (void)acceptCoinJoinQueueMessage:(NSData *)message { + DSLog(@"[OBJ-C] CoinJoin: got dsq"); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } - (void)acceptDarksendSessionMessage:(NSData *)message { } -- (void)acceptDarksendSessionUpdateMessage:(NSData *)message { +- (void)acceptCoinJoinStatusUpdateMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dssu"); + [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_STATUS_UPDATE]; } -- (void)acceptDarksendTransactionMessage:(NSData *)message { +- (void)acceptCoinJoinBroadcastTxMessage:(NSData *)message { // DSTransaction *tx = [DSTransaction transactionWithMessage:message]; // // if (! tx) { From 828e9da7d896b3c4f0c34c89cd5bf41678a1b3ca Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 21 Jun 2024 18:44:09 +0700 Subject: [PATCH 025/133] feat: masternodegroup improvements --- .../Models/CoinJoin/DSCoinJoinManager.h | 1 + .../Models/CoinJoin/DSCoinJoinManager.m | 16 ++- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 7 ++ .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 5 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 104 +++++++++++++++--- .../Managers/Chain Managers/DSPeerManager.m | 2 +- 7 files changed, 114 insertions(+), 22 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 1abedaaa9..68bc47788 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -36,6 +36,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @property (nonatomic, readonly) BOOL isWaitingForNewBlock; @property (nonatomic, readonly) BOOL isMixing; +@property (nonatomic, strong) id blocksObserver; + (instancetype)sharedInstanceForChain:(DSChain *)chain; - (instancetype)initWithChain:(DSChain *)chain; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 43c06c937..1bb072d94 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -72,6 +72,19 @@ - (void)startAsync { DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); [self.chain.chainManager.peerManager shouldSendDsq:true]; [_masternodeGroup startAsync]; + + self.blocksObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification + object:nil + queue:nil + usingBlock:^(NSNotification *note) { + if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) { + + DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); + [self.masternodeGroup triggerConnections]; + [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; + } + }]; } } @@ -80,6 +93,7 @@ - (void)stopAsync { [self.chain.chainManager.peerManager shouldSendDsq:false]; [_masternodeGroup stopAsync]; _masternodeGroup = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; } } @@ -90,8 +104,6 @@ - (void)runCoinJoin { - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { [_wrapper processDSQueueFrom:peer message:message]; - } else if ([type isEqualToString:MSG_COINJOIN_BROADCAST_TX]) { -// [_wrapper processBroadcastTxFrom:peer message:message]; } else { [_wrapper processMessageFrom:peer message:message type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index e7722a0b5..55161fd10 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message; - (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; +- (void)notifyNewBestBlock:(DSBlock *)block; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 3bea768d1..0558bef5b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -24,6 +24,7 @@ #import "DSAccount.h" #import "DSChainManager.h" #import "DSCoinJoinWrapper.h" +#import "DSBlock.h" #define AS_OBJC(context) ((__bridge DSCoinJoinWrapper *)(context)) #define AS_RUST(context) ((__bridge void *)(context)) @@ -123,6 +124,12 @@ - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSStri } } +- (void)notifyNewBestBlock:(DSBlock *)block { + if (block) { + notify_new_best_block(_clientManager, (uint8_t (*)[32])(block.blockHash.u8), block.height); + } +} + - (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId { return mixing_masternode_address(_clientManager, (uint8_t (*)[32])(clientSessionId.u8)); } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index 6eed389fb..a2294ebc3 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -17,15 +17,15 @@ #import #import "DSChain.h" +#import "DSPeer.h" NS_ASSUME_NONNULL_BEGIN @class DSCoinJoinManager; -@interface DSMasternodeGroup : NSObject +@interface DSMasternodeGroup : NSObject @property (atomic, readonly) BOOL isRunning; -@property (nonatomic, strong) id blocksObserver; @property (nonatomic, strong) DSChain *chain; @property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; @property (nonatomic, strong) NSMutableSet *pendingSessions; @@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)startAsync; - (void)stopAsync; +- (void)triggerConnections; - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port; - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index bfb987587..23dfc5722 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -34,6 +34,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _coinJoinManager = manager; _pendingSessions = [NSMutableSet set]; _masternodeMap = [NSMutableDictionary dictionary]; + _addressMap = [NSMutableDictionary dictionary]; _mutableConnectedPeers = [NSMutableSet set]; _mutablePendingPeers = [NSMutableSet set]; } @@ -42,17 +43,6 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { - (void)startAsync { _isRunning = true; - self.blocksObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification - object:nil - queue:nil - usingBlock:^(NSNotification *note) { - if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) { - - DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); - [self triggerConnections]; - } - }]; } - (dispatch_queue_t)networkingQueue { @@ -61,7 +51,6 @@ - (dispatch_queue_t)networkingQueue { - (void)stopAsync { _isRunning = false; - [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; } - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { @@ -96,8 +85,6 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { } - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate { - return predicate([_chain.chainManager.peerManager connectedPeer]); // TODO: finish peer management - NSMutableString *listOfPeers = [NSMutableString string]; for (DSPeer *peer in self.connectedPeers) { @@ -152,7 +139,7 @@ - (void)triggerConnections { return; } - + // TODO }); } @@ -186,7 +173,7 @@ - (void)triggerConnections { - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self) { - DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@", (unsigned long)self.maxConnections, uint256_hex(proTxHash)); + DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [_pendingSessions addObject:sessionIdValue]; @@ -202,7 +189,7 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session - (void)updateMaxConnections { _maxConnections = _pendingSessions.count; - NSUInteger connections = MIN(self.maxConnections, DEFAULT_COINJOIN_SESSIONS); + NSUInteger connections = MIN(_maxConnections, DEFAULT_COINJOIN_SESSIONS); DSLog(@"[OBJ-C] CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); [self updateMaxConnections:connections]; @@ -278,4 +265,87 @@ - (NSString *)hostFor:(UInt128)address { return @(inet_ntop(AF_INET6, &address, s, sizeof(s))); } +- (BOOL)connectTo:(DSPeer *)peer incrementMaxConnections:(BOOL)increment { + if (![self isMasternodeSessionByPeer:peer]) { + return NO; + } + + if ([self isNodeConnected:peer] || [self isNodePending:peer]) { + DSLog(@"[OBJ-C] CoinJoin: attempting to connect to the same masternode again: %@", peer.location); + return NO; // do not connect to the same peer again + } + + DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; + UInt256 sessionId = UINT256_ZERO; + + @synchronized (_masternodeMap) { + UInt256 proTxHash = mn.providerRegistrationTransactionHash; + NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; + NSValue *keyObject = [_masternodeMap objectForKey:proTxHashKey]; + + if (keyObject) { + [keyObject getValue:&sessionId]; + } + } + + if (uint256_is_zero(sessionId)) { + DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap"); + return NO; + } + + SocketAddress *mixingMasternodeAddress = [_coinJoinManager mixingMasternodeAddressFor:sessionId]; + + if (!mixingMasternodeAddress) { + DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, sessionId: %@", uint256_hex(sessionId)); + return NO; + } + + DSLog(@"[OBJ-C] CoinJoin: masternode[connecting] %@: %@; %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); + + [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.chain.chainManager.governanceSyncManager sporkDelegate:self.chain.chainManager.sporkManager masternodeDelegate:self.chain.chainManager.masternodeManager queue:self.networkingQueue]; + peer.earliestKeyTime = self.chain.earliestWalletCreationTime;; + + [self.mutablePendingPeers addObject:peer]; + [peer connect]; + + return YES; +} + +- (BOOL)isMasternodeSessionByPeer:(DSPeer *)peer { + @synchronized (_addressMap) { + return [_addressMap objectForKey:peer.location] != nil; + } +} + +- (BOOL)isNodeConnected:(DSPeer *)node { + return [self forPeer:node.address port:node.port warn:false withPredicate:^BOOL(DSPeer * _Nonnull peer) { + return YES; + }]; +} + +- (BOOL)isNodePending:(DSPeer *)node { + for (DSPeer *peer in self.mutablePendingPeers) { + + if (uint128_eq(node.address, peer.address) && node.port == peer.port) { + return YES; + } + } + + return NO; +} + +@synthesize downloadPeer; + +- (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { + // TODO +} + +- (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { + // TODO ? +} + +- (void)peerConnected:(nonnull DSPeer *)peer { + // TODO +} + @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index 48e4b0a9d..39acfa444 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -772,7 +772,7 @@ - (void)connect { DSPeer *peer = peers[(NSUInteger)(pow(arc4random_uniform((uint32_t)peers.count), 2) / peers.count)]; if (peer && ![self.connectedPeers containsObject:peer]) { - [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.transactionManager governanceDelegate:self.governanceSyncManager sporkDelegate:self.sporkManager masternodeDelegate:self.masternodeManager queue:self.networkingQueue]; + [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.governanceSyncManager sporkDelegate:self.sporkManager masternodeDelegate:self.masternodeManager queue:self.networkingQueue]; peer.earliestKeyTime = earliestWalletCreationTime; [self.mutableConnectedPeers addObject:peer]; From fd47f05b58f01e41563929c5ba25ca20a75e2aeb Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 21 Jun 2024 20:18:01 +0700 Subject: [PATCH 026/133] feat: connected/disconnected --- .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 2 + .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 65 ++++++++++++++----- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index a2294ebc3..7c19941a1 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -36,6 +36,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) NSLock *lock; @property (nonatomic, strong) NSMutableSet *mutableConnectedPeers; @property (nonatomic, strong) NSMutableSet *mutablePendingPeers; +@property (nonatomic, readonly) BOOL shouldSendDsq; +@property (nullable, nonatomic, readonly) DSPeer *downloadPeer; - (instancetype)initWithManager:(DSCoinJoinManager *)manager; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 23dfc5722..e000a9618 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -22,6 +22,7 @@ #import "DSSimplifiedMasternodeEntry.h" #import "DSMasternodeManager.h" #import "DSPeerManager.h" +#import "DSSendCoinJoinQueue.h" #import uint64_t const MIN_PEER_DISCOVERY_INTERVAL = 1000; @@ -37,6 +38,9 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _addressMap = [NSMutableDictionary dictionary]; _mutableConnectedPeers = [NSMutableSet set]; _mutablePendingPeers = [NSMutableSet set]; + _downloadPeer = nil; + _maxConnections = 0; + _shouldSendDsq = true; } return self; } @@ -73,11 +77,12 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); - [self.lock lock]; - [self.pendingClosingMasternodes addObject:peer]; - // TODO (dashj): what if this disconnects the wrong one - [self updateMaxConnections]; - [self.lock unlock]; + @synchronized (self.pendingClosingMasternodes) { + [self.pendingClosingMasternodes addObject:peer]; + // TODO (dashj): what if this disconnects the wrong one + [self updateMaxConnections]; + } + [peer disconnect]; return true; @@ -89,7 +94,7 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B for (DSPeer *peer in self.connectedPeers) { [listOfPeers appendFormat:@"%@, ", peer.location]; - + if (uint128_eq(peer.address, ip) && peer.port == port) { return predicate(peer); } @@ -172,7 +177,7 @@ - (void)triggerConnections { } - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { - @synchronized (self) { + @synchronized (_pendingSessions) { DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [_pendingSessions addObject:sessionIdValue]; @@ -305,7 +310,10 @@ - (BOOL)connectTo:(DSPeer *)peer incrementMaxConnections:(BOOL)increment { [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.chain.chainManager.governanceSyncManager sporkDelegate:self.chain.chainManager.sporkManager masternodeDelegate:self.chain.chainManager.masternodeManager queue:self.networkingQueue]; peer.earliestKeyTime = self.chain.earliestWalletCreationTime;; - [self.mutablePendingPeers addObject:peer]; + @synchronized (self.mutablePendingPeers) { + [self.mutablePendingPeers addObject:peer]; + } + [peer connect]; return YES; @@ -324,28 +332,49 @@ - (BOOL)isNodeConnected:(DSPeer *)node { } - (BOOL)isNodePending:(DSPeer *)node { - for (DSPeer *peer in self.mutablePendingPeers) { - - if (uint128_eq(node.address, peer.address) && node.port == peer.port) { - return YES; + @synchronized (self) { + for (DSPeer *peer in self.mutablePendingPeers) { + if (uint128_eq(node.address, peer.address) && node.port == peer.port) { + return YES; + } } } return NO; } -@synthesize downloadPeer; +- (void)peerConnected:(nonnull DSPeer *)peer { + // TODO: exp backoff + + @synchronized (_mutableConnectedPeers) { + [_mutablePendingPeers removeObject:peer]; + [_mutableConnectedPeers addObject:peer]; + } + + if (_shouldSendDsq) { + [peer sendRequest:[DSSendCoinJoinQueue requestWithShouldSend:true]]; + } +} - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { - // TODO + // TODO: exp backoff + + @synchronized (_mutableConnectedPeers) { + [_mutablePendingPeers removeObject:peer]; + [_mutableConnectedPeers removeObject:peer]; + + DSLog(@"[OBJ-C] CoinJoin: Peer died: %@ (%lu connected, %lu pending, %lu max)", peer.location, (unsigned long)_mutableConnectedPeers.count, (unsigned long)_mutablePendingPeers.count, (unsigned long)_maxConnections); + + NSUInteger numPeers = _mutablePendingPeers.count + _mutableConnectedPeers.count; + + if (numPeers < _maxConnections) { + [self triggerConnections]; + } + } } - (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { // TODO ? } -- (void)peerConnected:(nonnull DSPeer *)peer { - // TODO -} - @end From 772daeef5254b8c55095dfe14760bf1758020f04 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 28 Jun 2024 14:02:26 +0700 Subject: [PATCH 027/133] feat: process dsf --- .../Models/CoinJoin/DSCoinJoinManager.m | 9 +++- .../Models/CoinJoin/DSCoinJoinWrapper.m | 6 ++- .../CoinJoin/DSCoinJoinSignedInputs.h | 32 ++++++++++++ .../CoinJoin/DSCoinJoinSignedInputs.m | 47 +++++++++++++++++ DashSync/shared/Models/Network/DSPeer.h | 5 +- DashSync/shared/Models/Network/DSPeer.m | 50 +++---------------- 6 files changed, 99 insertions(+), 50 deletions(-) create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.h create mode 100644 DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.m diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 1bb072d94..6d662e818 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -27,6 +27,8 @@ #import "DSTransactionManager.h" #import "DSMasternodeManager.h" #import "DSCoinJoinAcceptMessage.h" +#import "DSCoinJoinEntryMessage.h" +#import "DSCoinJoinSignedInputs.h" #import "DSPeerManager.h" #import "DSSimplifiedMasternodeEntry.h" @@ -541,8 +543,11 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; [peer sendRequest:request]; - } else if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { - DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; + } else if ([messageType isEqualToString:DSCoinJoinEntryMessage.type]) { + DSCoinJoinEntryMessage *request = [DSCoinJoinEntryMessage requestWithData:message]; + [peer sendRequest:request]; + } else if ([messageType isEqualToString:DSCoinJoinSignedInputs.type]) { + DSCoinJoinSignedInputs *request = [DSCoinJoinSignedInputs requestWithData:message]; [peer sendRequest:request]; } else { DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 0558bef5b..58b6df3b5 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -345,7 +345,7 @@ ByteArray freshCoinJoinAddress(bool internal, const void *context) { return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); } -bool commitTransaction(struct Recipient **items, uintptr_t item_count, const void *context) { +bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_denominating, const void *context) { DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); NSMutableArray *amounts = [NSMutableArray array]; @@ -364,7 +364,9 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, const voi @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { - if (!error) { + if (error) { + DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@", error); + } else if (is_denominating) { DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); bool isFinished = finish_automatic_denominating(wrapper.clientManager); DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.h b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.h new file mode 100644 index 000000000..24b22b09b --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.h @@ -0,0 +1,32 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSMessageRequest.h" +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface DSCoinJoinSignedInputs : DSMessageRequest + +@property (nonatomic, readonly) NSData *data; + ++ (instancetype)requestWithData:(NSData *)data; ++ (NSString *)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.m b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.m new file mode 100644 index 000000000..2a6e2bb83 --- /dev/null +++ b/DashSync/shared/Models/Messages/CoinJoin/DSCoinJoinSignedInputs.m @@ -0,0 +1,47 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSCoinJoinSignedInputs.h" +#import "DSPeer.h" + +@implementation DSCoinJoinSignedInputs + ++ (instancetype)requestWithData:(NSData *)data { + return [[DSCoinJoinSignedInputs alloc] initWithData:data]; +} + ++ (NSString *)type { + return MSG_COINJOIN_SIGNED_INPUTS; +} + +- (instancetype)initWithData:(NSData *)data { + self = [super init]; + if (self) { + _data = data; + } + return self; +} + +- (NSString *)type { + return DSCoinJoinSignedInputs.type; +} + +- (NSData *)toData { + return self.data; +} +@end diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index 190cf726f..e487926d9 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -145,9 +145,8 @@ typedef NS_ENUM(uint32_t, DSInvType) #define MSG_COINJOIN_BROADCAST_TX @"dstx" #define MSG_COINJOIN_STATUS_UPDATE @"dssu" #define MSG_COINJOIN_COMPLETE @"dsc" - -#define MSG_DARKSENDFINISH @"dsf" -#define MSG_DARKSENDSESSION @"dss" +#define MSG_COINJOIN_FINAL_TRANSACTION @"dsf" +#define MSG_COINJOIN_SIGNED_INPUTS @"dss" #define REJECT_INVALID 0x10 // transaction is invalid for some reason (invalid signature, output value > input, etc) #define REJECT_SPENT 0x12 // an input is already spent diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 4678ed3f0..9a43662f8 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -846,12 +846,10 @@ - (void)acceptMessage:(NSData *)message type:(NSString *)type { // CoinJoin else if ([MSG_COINJOIN_COMPLETE isEqual:type]) [self acceptCoinJoinCompleteMessage:message]; - else if ([MSG_DARKSENDFINISH isEqual:type]) - [self acceptDarksendFinishMessage:message]; + else if ([MSG_COINJOIN_FINAL_TRANSACTION isEqual:type]) + [self acceptCoinJoinFinalTransaction:message]; else if ([MSG_COINJOIN_QUEUE isEqual:type]) [self acceptCoinJoinQueueMessage:message]; - else if ([MSG_DARKSENDSESSION isEqual:type]) - [self acceptDarksendSessionMessage:message]; else if ([MSG_COINJOIN_STATUS_UPDATE isEqual:type]) [self acceptCoinJoinStatusUpdateMessage:message]; else if ([MSG_COINJOIN_BROADCAST_TX isEqual:type]) @@ -1815,18 +1813,16 @@ - (void)acceptGovObjectSyncMessage:(NSData *)message { DSLogWithLocation(self, @"Gov Object Sync"); } -// MARK: - Accept Dark send - -- (void)acceptDarksendAnnounceMessage:(NSData *)message { -} +// MARK: - Accept CoinJoin messages - (void)acceptCoinJoinCompleteMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsc"); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_COMPLETE]; } -- (void)acceptDarksendFinishMessage:(NSData *)message { +- (void)acceptCoinJoinFinalTransaction:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsf"); + [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_FINAL_TRANSACTION]; } - (void)acceptCoinJoinQueueMessage:(NSData *)message { @@ -1834,46 +1830,14 @@ - (void)acceptCoinJoinQueueMessage:(NSData *)message { [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } -- (void)acceptDarksendSessionMessage:(NSData *)message { -} - - (void)acceptCoinJoinStatusUpdateMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dssu"); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_STATUS_UPDATE]; } - (void)acceptCoinJoinBroadcastTxMessage:(NSData *)message { - // DSTransaction *tx = [DSTransaction transactionWithMessage:message]; - // - // if (! tx) { - // [self error:@"malformed tx message: %@", message]; - // return; - // } - // else if (! self.sentFilter && ! self.sentTxAndBlockGetdata) { - // [self error:@"got tx message before loading a filter"]; - // return; - // } - // - // DSLogPrivate(@"%@:%u got tx %@", self.host, self.port, uint256_obj(tx.txHash)); - // - // dispatch_async(self.delegateQueue, ^{ - // [self.delegate peer:self relayedTransaction:tx]; - // }); - // - // if (self.currentBlock) { // we're collecting tx messages for a merkleblock - // [self.currentBlockTxHashes removeObject:uint256_obj(tx.txHash)]; - // - // if (self.currentBlockTxHashes.count == 0) { // we received the entire block including all matched tx - // BRMerkleBlock *block = self.currentBlock; - // - // self.currentBlock = nil; - // self.currentBlockTxHashes = nil; - // - // dispatch_sync(self.delegateQueue, ^{ // syncronous dispatch so we don't get too many queued up tx - // [self.delegate peer:self relayedBlock:block]; - // }); - // } - // } + DSLog(@"[OBJ-C] CoinJoin: got dstx"); + [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_BROADCAST_TX]; } // MARK: - hash From b5e42b52acf00064b75d6bb08e390e7bf819607c Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 2 Jul 2024 14:53:59 +0700 Subject: [PATCH 028/133] feat: connectivity management --- DashSync/shared/Models/CoinJoin/DSBackoff.h | 33 +++ DashSync/shared/Models/CoinJoin/DSBackoff.m | 43 ++++ .../CoinJoin/{Model => }/DSCoinControl.h | 0 .../CoinJoin/{Model => }/DSCoinControl.m | 0 .../Models/CoinJoin/DSCoinJoinManager.m | 2 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 5 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 12 - .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 220 ++++++++++++++++-- .../Managers/Chain Managers/DSPeerManager.h | 1 - .../Managers/Chain Managers/DSPeerManager.m | 4 - 10 files changed, 280 insertions(+), 40 deletions(-) create mode 100644 DashSync/shared/Models/CoinJoin/DSBackoff.h create mode 100644 DashSync/shared/Models/CoinJoin/DSBackoff.m rename DashSync/shared/Models/CoinJoin/{Model => }/DSCoinControl.h (100%) rename DashSync/shared/Models/CoinJoin/{Model => }/DSCoinControl.m (100%) diff --git a/DashSync/shared/Models/CoinJoin/DSBackoff.h b/DashSync/shared/Models/CoinJoin/DSBackoff.h new file mode 100644 index 000000000..4d023e9a2 --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSBackoff.h @@ -0,0 +1,33 @@ +// +// Created by Andrei Ashikhmin +// 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 + +@interface DSBackoff : NSObject + +@property (nonatomic, strong) NSDate *retryTime; +@property (nonatomic, assign) float_t backoff; +@property (nonatomic, readonly) float_t maxBackoff; +@property (nonatomic, readonly) float_t initialBackoff; +@property (nonatomic, readonly) float_t multiplier; + +- (instancetype)initInitialBackoff:(float_t)initial maxBackoff:(float_t)max multiplier:(float_t)multiplier; + +- (void)trackSuccess; +- (void)trackFailure; + +@end diff --git a/DashSync/shared/Models/CoinJoin/DSBackoff.m b/DashSync/shared/Models/CoinJoin/DSBackoff.m new file mode 100644 index 000000000..8954cef7b --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSBackoff.m @@ -0,0 +1,43 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSBackoff.h" + +@implementation DSBackoff + +- (instancetype)initInitialBackoff:(float_t)initial maxBackoff:(float_t)max multiplier:(float_t)multiplier { + self = [super init]; + if (self) { + _maxBackoff = max; + _initialBackoff = initial; + _multiplier = multiplier; + [self trackSuccess]; + } + return self; +} + +- (void)trackSuccess { + _backoff = _initialBackoff; + _retryTime = [NSDate date]; +} + +- (void)trackFailure { + _retryTime = [[NSDate date] dateByAddingTimeInterval:_backoff]; + _backoff = MIN(_backoff, _maxBackoff); +} + +@end diff --git a/DashSync/shared/Models/CoinJoin/Model/DSCoinControl.h b/DashSync/shared/Models/CoinJoin/DSCoinControl.h similarity index 100% rename from DashSync/shared/Models/CoinJoin/Model/DSCoinControl.h rename to DashSync/shared/Models/CoinJoin/DSCoinControl.h diff --git a/DashSync/shared/Models/CoinJoin/Model/DSCoinControl.m b/DashSync/shared/Models/CoinJoin/DSCoinControl.m similarity index 100% rename from DashSync/shared/Models/CoinJoin/Model/DSCoinControl.m rename to DashSync/shared/Models/CoinJoin/DSCoinControl.m diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 6d662e818..9351f3483 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -269,7 +269,7 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { return total; } -- (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount { +- (NSArray *)availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount { NSMutableArray *vCoins = [NSMutableArray array]; @synchronized(self) { diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 58b6df3b5..f25120f95 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -345,7 +345,7 @@ ByteArray freshCoinJoinAddress(bool internal, const void *context) { return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); } -bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_denominating, const void *context) { +bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_denominating, uint8_t (*client_session_id)[32], const void *context) { DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); NSMutableArray *amounts = [NSMutableArray array]; @@ -368,7 +368,8 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_d DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@", error); } else if (is_denominating) { DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); - bool isFinished = finish_automatic_denominating(wrapper.clientManager); + bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); + processor_destroy_block_hash(client_session_id); DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); } }]; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index 7c19941a1..1dcc43316 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -26,18 +26,6 @@ NS_ASSUME_NONNULL_BEGIN @interface DSMasternodeGroup : NSObject @property (atomic, readonly) BOOL isRunning; -@property (nonatomic, strong) DSChain *chain; -@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; -@property (nonatomic, strong) NSMutableSet *pendingSessions; -@property (nonatomic, strong) NSMutableDictionary *masternodeMap; -@property (nonatomic, strong) NSMutableDictionary *addressMap; -@property (atomic, readonly) NSUInteger maxConnections; -@property (nonatomic, strong) NSMutableArray *pendingClosingMasternodes; -@property (nonatomic, strong) NSLock *lock; -@property (nonatomic, strong) NSMutableSet *mutableConnectedPeers; -@property (nonatomic, strong) NSMutableSet *mutablePendingPeers; -@property (nonatomic, readonly) BOOL shouldSendDsq; -@property (nullable, nonatomic, readonly) DSPeer *downloadPeer; - (instancetype)initWithManager:(DSCoinJoinManager *)manager; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index e000a9618..86a8314b7 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -23,9 +23,35 @@ #import "DSMasternodeManager.h" #import "DSPeerManager.h" #import "DSSendCoinJoinQueue.h" +#import "DSBackoff.h" #import -uint64_t const MIN_PEER_DISCOVERY_INTERVAL = 1000; +float_t const MIN_PEER_DISCOVERY_INTERVAL = 1; // One second +float_t const DEFAULT_INITIAL_BACKOFF = 1; // One second +float_t const DEFAULT_MAX_BACKOFF = 5; // Five seconds +float_t const GROUP_BACKOFF_MULTIPLIER = 1.5; +float_t const BACKOFF_MULTIPLIER = 1.001; + +@interface DSMasternodeGroup () + +@property (nonatomic, strong) DSChain *chain; +@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; +@property (nonatomic, strong) NSMutableSet *pendingSessions; +@property (nonatomic, strong) NSMutableDictionary *masternodeMap; +@property (nonatomic, strong) NSMutableDictionary *addressMap; +@property (atomic, readonly) NSUInteger maxConnections; +@property (nonatomic, strong) NSMutableArray *pendingClosingMasternodes; +@property (nonatomic, strong) NSMutableSet *mutableConnectedPeers; +@property (nonatomic, strong) NSMutableSet *mutablePendingPeers; +@property (nonatomic, readonly) BOOL shouldSendDsq; +@property (nullable, nonatomic, readwrite) DSPeer *downloadPeer; +@property (nonatomic, readonly) NSUInteger backoff; +@property (nonatomic, strong) DSBackoff *groupBackoff; +@property (nonatomic, strong) NSMutableDictionary *backoffMap; +@property (nonatomic, strong) NSMutableArray *inactives; +@property (nonatomic, strong) NSLock *lock; + +@end @implementation DSMasternodeGroup @@ -34,6 +60,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { if (self) { _coinJoinManager = manager; _pendingSessions = [NSMutableSet set]; + _pendingClosingMasternodes = [NSMutableArray array]; _masternodeMap = [NSMutableDictionary dictionary]; _addressMap = [NSMutableDictionary dictionary]; _mutableConnectedPeers = [NSMutableSet set]; @@ -41,6 +68,9 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _downloadPeer = nil; _maxConnections = 0; _shouldSendDsq = true; + _groupBackoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:GROUP_BACKOFF_MULTIPLIER]; + _backoffMap = [NSMutableDictionary dictionary]; + _inactives = [NSMutableArray array]; } return self; } @@ -135,7 +165,12 @@ - (NSSet *)pendingPeers { } - (void)triggerConnections { - dispatch_async(self.networkingQueue, ^{ + [self triggerConnectionsJobWithDelay:0]; +} + +- (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { + dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); + dispatch_after(delayTime, self.networkingQueue, ^{ if (!self.isRunning) { return; } @@ -144,7 +179,73 @@ - (void)triggerConnections { return; } - // TODO + BOOL doDiscovery = NO; + NSDate *now = [NSDate date]; + + @synchronized (self.inactives) { + BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; + doDiscovery = !havPeersToTry; + } + + @synchronized (self.inactives) { + NSUInteger numPeers = self.mutablePendingPeers.count + self.connectedPeers.count; + DSPeer *peerToTry = nil; + NSDate *retryTime = nil; + + if (doDiscovery) { + NSArray *peers = [self getPeers]; + + for (DSPeer *peer in [self getPeers]) { + [self addInactive:peer]; + } + + BOOL discoverySuccess = peers.count > 0; + // Require that we have enough connections, to consider this + // a success, or we just constantly test for new peers + if (discoverySuccess && numPeers >= self.maxConnections) { + [self.groupBackoff trackSuccess]; + } else { + [self.groupBackoff trackFailure]; + } + } + + // Inactives is sorted by backoffMap time. + if (self.inactives.count == 0) { + if (numPeers < self.maxConnections) { + NSTimeInterval interval = MAX([self.groupBackoff.retryTime timeIntervalSinceDate:now], MIN_PEER_DISCOVERY_INTERVAL); + + DSLog(@"[OBJ-C] CoinJoin: Masternode discovery didn't provide us any more masternodes, will try again in %fl ms.", interval); + + [self triggerConnectionsJobWithDelay:interval]; + } else { + // We have enough peers and discovery provided no more, so just settle down. Most likely we + // were given a fixed set of addresses in some test scenario. + } + return; + } else { + peerToTry = self.inactives.firstObject; + [self.inactives removeObjectAtIndex:0]; + retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; + } + + retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; + + if (retryTime > now) { + NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; + DSLog(@"Waiting %fl ms before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); + [self.inactives addObject:peerToTry]; + [self triggerConnectionsJobWithDelay:delay]; + return; + } + + [self connectTo:peerToTry incrementMaxConnections:false]; + } + + NSUInteger count = self.mutablePendingPeers.count + self.connectedPeers.count; + + if (count < self.maxConnections) { + [self triggerConnectionsJobWithDelay:0]; // Try next peer immediately. + } }); } @@ -207,14 +308,29 @@ - (void)updateMaxConnections:(NSUInteger)connections { return; } - // TODO: // We may now have too many or too few open connections. Add more or drop some to get to the right amount. -// adjustment = maxConnections - channels.getConnectedClientCount(); -// if (adjustment > 0) -// triggerConnections(); -// -// if (adjustment < 0) -// channels.closeConnections(-adjustment); + NSUInteger adjustment = 0; + NSSet *connectedPeers = self.connectedPeers; + + @synchronized (self.mutablePendingPeers) { + NSUInteger numPeers = _mutablePendingPeers.count + connectedPeers.count; + adjustment = _maxConnections - numPeers; + } + + if (adjustment > 0) { + [self triggerConnections]; + } + + if (adjustment < 0) { + for (DSPeer *peer in connectedPeers) { + [peer disconnect]; + adjustment++; + + if (adjustment >= 0) { + break; + } + } + } } - (void)checkMasternodesWithoutSessions { @@ -344,9 +460,12 @@ - (BOOL)isNodePending:(DSPeer *)node { } - (void)peerConnected:(nonnull DSPeer *)peer { - // TODO: exp backoff - - @synchronized (_mutableConnectedPeers) { + @synchronized (self) { + [_groupBackoff trackSuccess]; + [[_backoffMap objectForKey:peer.location] trackSuccess]; + + DSLog(@"[OBJ-C] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, _mutableConnectedPeers.count, _mutablePendingPeers.count, _maxConnections); + [_mutablePendingPeers removeObject:peer]; [_mutableConnectedPeers addObject:peer]; } @@ -356,25 +475,86 @@ - (void)peerConnected:(nonnull DSPeer *)peer { } } -- (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { - // TODO: exp backoff - - @synchronized (_mutableConnectedPeers) { +- (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { + @synchronized (self) { [_mutablePendingPeers removeObject:peer]; [_mutableConnectedPeers removeObject:peer]; - + DSLog(@"[OBJ-C] CoinJoin: Peer died: %@ (%lu connected, %lu pending, %lu max)", peer.location, (unsigned long)_mutableConnectedPeers.count, (unsigned long)_mutablePendingPeers.count, (unsigned long)_maxConnections); - + + [_groupBackoff trackFailure]; + [[_backoffMap objectForKey:peer.location] trackFailure]; + // Put back on inactive list + [self addInactive:peer]; NSUInteger numPeers = _mutablePendingPeers.count + _mutableConnectedPeers.count; if (numPeers < _maxConnections) { [self triggerConnections]; } } + + @synchronized (_pendingClosingMasternodes) { + DSPeer *masternode = NULL; + + for (DSPeer *mn in _pendingClosingMasternodes) { + if ([peer.location isEqualToString:mn.location]) { + masternode = mn; + } + } + + DSLog(@"[OBJ-C] CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + + if (masternode) { + NSString *address = peer.location; + + if ([_pendingClosingMasternodes containsObject:masternode]) { + // if this is part of pendingClosingMasternodes, where we want to close the connection, + // we don't want to increase the backoff time + [[_backoffMap objectForKey:address] trackSuccess]; + } + + [_pendingClosingMasternodes removeObject:masternode]; + UInt256 sessionId = [_chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; + NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; + [_pendingSessions removeObject:sessionIdValue]; + [_masternodeMap removeObjectForKey:sessionIdValue]; + [_addressMap removeObjectForKey:masternode.location]; + } + + [self checkMasternodesWithoutSessions]; + } } -- (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { +- (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { // TODO ? } +- (void)addInactive:(DSPeer *)peer { + @synchronized (_inactives) { + // Deduplicate, handle differently than PeerGroup + if ([self.inactives containsObject:peer]) { + return; + } + + // do not connect to another the same peer twice + if ([self isNodeConnected:peer]) { + DSLog(@"[OBJ-C] CoinJoin: attempting to connect to the same masternode again: %@", peer.location); + return; + } + + DSBackoff *backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:GROUP_BACKOFF_MULTIPLIER]; + [self.backoffMap setObject:backoff forKey:peer.location]; + [self.inactives addObject:peer]; + [self sortInactives]; + } +} + +- (void)sortInactives { + [_inactives sortUsingComparator:^NSComparisonResult(DSPeer *obj1, DSPeer *obj2) { + DSBackoff *backoff1 = [_backoffMap objectForKey:obj1.location]; + DSBackoff *backoff2 = [_backoffMap objectForKey:obj2.location]; + return [backoff1.retryTime compare:backoff2.retryTime]; + }]; +} + @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h index 92ed589be..9064eb275 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h @@ -95,7 +95,6 @@ typedef NS_ENUM(uint16_t, DSDisconnectReason) // MARK: CoinJoin -- (DSPeer *)connectedPeer; // TODO(coinjoin): temp - (void)shouldSendDsq:(BOOL)shouldSendDsq; @end diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index 39acfa444..498b8fdd2 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -1088,10 +1088,6 @@ - (void)sendRequest:(DSMessageRequest *)request { // MARK: CoinJoin -- (DSPeer *)connectedPeer { // TODO(coinjoin): temp - return self.connectedPeers.objectEnumerator.nextObject; -} - - (void)shouldSendDsq:(BOOL)shouldSendDsq { for (DSPeer *peer in self.connectedPeers) { DSSendCoinJoinQueue *request = [DSSendCoinJoinQueue requestWithShouldSend:shouldSendDsq]; From ff080d85551489997c72524067b0b196688ccd57 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 2 Jul 2024 19:29:54 +0700 Subject: [PATCH 029/133] fix: remove redundant synchronize --- DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 86a8314b7..e9f2a7f49 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -185,9 +185,6 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized (self.inactives) { BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; doDiscovery = !havPeersToTry; - } - - @synchronized (self.inactives) { NSUInteger numPeers = self.mutablePendingPeers.count + self.connectedPeers.count; DSPeer *peerToTry = nil; NSDate *retryTime = nil; From 4b4c6fd7e123420dca32fe440e8eeb05a3f32487 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 11 Jul 2024 14:47:51 +0700 Subject: [PATCH 030/133] fix: reorg and bug fixes --- .../Models/CoinJoin/DSCoinJoinManager.h | 7 +- .../Models/CoinJoin/DSCoinJoinManager.m | 108 ++++++++++++++---- .../Models/CoinJoin/DSCoinJoinWrapper.h | 6 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 44 +++++-- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 19 ++- DashSync/shared/Models/Network/DSPeer.m | 2 +- Example/DashSync/DSCoinJoinViewController.m | 11 +- 7 files changed, 156 insertions(+), 41 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 68bc47788..179636d8c 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -52,15 +52,18 @@ NS_ASSUME_NONNULL_BEGIN - (DSMasternodeList *)mnList; - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port; - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; -- (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port; +- (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port warn:(BOOL)warn; - (Balance *)getBalance; - (void)startAsync; - (void)stopAsync; -- (void)runCoinJoin; +- (void)start; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; +- (void)setStopOnNothingToDo:(BOOL)stop; +- (BOOL)startMixing; +- (void)doAutomaticDenominating; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 9351f3483..ab153d873 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -31,10 +31,19 @@ #import "DSCoinJoinSignedInputs.h" #import "DSPeerManager.h" #import "DSSimplifiedMasternodeEntry.h" +#import "DSChain+Protected.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; +@interface DSCoinJoinManager () + +@property (nonatomic, strong) dispatch_group_t processingGroup; +@property (nonatomic, strong) dispatch_queue_t processingQueue; +@property (nonatomic, strong) dispatch_source_t coinjoinTimer; + +@end + @implementation DSCoinJoinManager static NSMutableDictionary *_managerChainDictionary = nil; @@ -64,53 +73,108 @@ - (instancetype)initWithChain:(DSChain *)chain { _chain = chain; _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chain.chainManager]; _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; + _processingGroup = dispatch_group_create(); + _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.coinjoin.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); } return self; } - (void)startAsync { - if (!_masternodeGroup.isRunning) { - DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); - [self.chain.chainManager.peerManager shouldSendDsq:true]; - [_masternodeGroup startAsync]; - + if (!self.masternodeGroup.isRunning) { self.blocksObserver = [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification object:nil queue:nil usingBlock:^(NSNotification *note) { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) { - - DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); - [self.masternodeGroup triggerConnections]; [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; } }]; + + DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); + [self.chain.chainManager.peerManager shouldSendDsq:true]; + [self.masternodeGroup startAsync]; + } +} + +- (void)start { + DSLog(@"[OBJ-C] CoinJoinManager starting..."); + [self cancelCoinjoinTimer]; + uint32_t interval = 1; + uint32_t delay = 1; + + @synchronized (self) { + [self.wrapper registerCoinJoin]; + self.coinjoinTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.processingQueue); + if (self.coinjoinTimer) { + dispatch_source_set_timer(self.coinjoinTimer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), interval * NSEC_PER_SEC, 1ull * NSEC_PER_SEC); + dispatch_source_set_event_handler(self.coinjoinTimer, ^{ + DSLog(@"[OBJ-C] CoinJoin: trigger doMaintenance"); + [self.wrapper doMaintenance]; + }); + dispatch_resume(self.coinjoinTimer); + } + } +} + +- (BOOL)startMixing { + @synchronized (self.wrapper) { + return [self.wrapper startMixing]; } } - (void)stopAsync { - if (_masternodeGroup != nil && _masternodeGroup.isRunning) { - [self.chain.chainManager.peerManager shouldSendDsq:false]; - [_masternodeGroup stopAsync]; - _masternodeGroup = nil; - [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; + if (self.masternodeGroup != nil && self.masternodeGroup.isRunning) { + DSLog(@"[OBJ-C] CoinJoin: call stop"); + [self.chain.chainManager.peerManager shouldSendDsq:false]; + [self.masternodeGroup stopAsync]; + self.masternodeGroup = nil; + [self cancelCoinjoinTimer]; + [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; } } -- (void)runCoinJoin { - [_wrapper runCoinJoin]; +- (void)doAutomaticDenominating { + dispatch_async(self.processingQueue, ^{ + dispatch_group_enter(self.processingGroup); + + @synchronized (self.wrapper) { + [self.wrapper doAutomaticDenominating]; + } + + dispatch_group_leave(self.processingGroup); + }); } -- (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { - if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { - [_wrapper processDSQueueFrom:peer message:message]; - } else { - [_wrapper processMessageFrom:peer message:message type:type]; +- (void)cancelCoinjoinTimer { + @synchronized (self) { + if (self.coinjoinTimer) { + dispatch_source_cancel(self.coinjoinTimer); + self.coinjoinTimer = nil; + } } } +- (void)setStopOnNothingToDo:(BOOL)stop { + @synchronized (self.wrapper) { + [self.wrapper setStopOnNothingToDo:stop]; + } +} + +- (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { + dispatch_async(self.processingQueue, ^{ + dispatch_group_enter(self.processingGroup); + + if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { + [self.wrapper processDSQueueFrom:peer message:message]; + } else { + [self.wrapper processMessageFrom:peer message:message type:type]; + } + + dispatch_group_leave(self.processingGroup); + }); +} - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { DSTransaction *tx = [self.chain transactionForHash:txHash]; @@ -538,8 +602,8 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [_masternodeGroup disconnectMasternode:ip port:port]; } -- (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port { - return [self.masternodeGroup forPeer:address port:port warn:true withPredicate:^BOOL(DSPeer * _Nonnull peer) { +- (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port warn:(BOOL)warn { + return [self.masternodeGroup forPeer:address port:port warn:warn withPredicate:^BOOL(DSPeer * _Nonnull peer) { if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; [peer sendRequest:request]; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 55161fd10..c1b3840e7 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -34,13 +34,17 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; -- (void)runCoinJoin; - (BOOL)isWaitingForNewBlock; - (BOOL)isMixing; - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message; - (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)notifyNewBestBlock:(DSBlock *)block; +- (void)setStopOnNothingToDo:(BOOL)stop; +- (BOOL)startMixing; +- (BOOL)doAutomaticDenominating; +- (void)doMaintenance; +- (void)registerCoinJoin; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index f25120f95..9d858f252 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -40,14 +40,14 @@ - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSCh return self; } -- (void)runCoinJoin { +- (void)registerCoinJoin { if (_options == NULL) { _options = [self createOptions]; } if (_walletEx == NULL) { DSLog(@"[OBJ-C] CoinJoin: register wallet ex"); - _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode); + _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync); } if (_clientManager == NULL) { @@ -76,12 +76,34 @@ - (CoinJoinClientOptions *)createOptions { options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; - options->coinjoin_multi_session = NO; + options->coinjoin_multi_session = YES; DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.chainManager.chain.balance); return options; } +- (void)setStopOnNothingToDo:(BOOL)stop { + set_stop_on_nothing_to_do(self.clientManager, stop); +} + +- (BOOL)startMixing { + return start_mixing(self.clientManager); +} + +- (BOOL)doAutomaticDenominating { + Balance *balance = [self.manager getBalance]; + BOOL result = do_automatic_denominating(_clientManager, *balance, false); + free(balance); + + return result; +} + +- (void)doMaintenance { + Balance *balance = [self.manager getBalance]; + do_maintenance(_clientManager, *balance); + free(balance); +} + - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { @synchronized (self) { ByteArray *array = malloc(sizeof(ByteArray)); @@ -97,12 +119,6 @@ - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { free(array); } - - DSLog(@"[OBJ-C] CoinJoin: call"); - Balance *balance = [self.manager getBalance]; - - run_client_manager(_clientManager, *balance); - free(balance); } } @@ -463,14 +479,14 @@ bool disconnectMasternode(uint8_t (*ip_address)[16], uint16_t port, const void * return result; } -bool sendMessage(char *message_type, ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, const void *context) { +bool sendMessage(char *message_type, ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, bool warn, const void *context) { NSString *messageType = [NSString stringWithUTF8String:message_type]; UInt128 ipAddress = *((UInt128 *)ip_address); BOOL result = YES; @synchronized (context) { NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; - result = [AS_OBJC(context).manager sendMessageOfType:messageType message:message withPeerIP:ipAddress port:port]; + result = [AS_OBJC(context).manager sendMessageOfType:messageType message:message withPeerIP:ipAddress port:port warn:warn]; } return result; @@ -488,4 +504,10 @@ bool addPendingMasternode(uint8_t (*pro_tx_hash)[32], uint8_t (*session_id)[32], return result; } +void startManagerAsync(const void *context) { + @synchronized (context) { + [AS_OBJC(context).manager startAsync]; + } +} + @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index e9f2a7f49..9752d976d 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -35,7 +35,7 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) DSChain *chain; -@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; +@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; // TODO: sync all access points @property (nonatomic, strong) NSMutableSet *pendingSessions; @property (nonatomic, strong) NSMutableDictionary *masternodeMap; @property (nonatomic, strong) NSMutableDictionary *addressMap; @@ -50,6 +50,7 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) NSMutableDictionary *backoffMap; @property (nonatomic, strong) NSMutableArray *inactives; @property (nonatomic, strong) NSLock *lock; +@property (nonatomic, strong) id blocksObserver; @end @@ -59,6 +60,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { self = [super init]; if (self) { _coinJoinManager = manager; + _chain = manager.chain; _pendingSessions = [NSMutableSet set]; _pendingClosingMasternodes = [NSMutableArray array]; _masternodeMap = [NSMutableDictionary dictionary]; @@ -77,6 +79,18 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { - (void)startAsync { _isRunning = true; + + self.blocksObserver = + [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification + object:nil + queue:nil + usingBlock:^(NSNotification *note) { + if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) { + + DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); + [self triggerConnections]; + } + }]; } - (dispatch_queue_t)networkingQueue { @@ -85,6 +99,7 @@ - (dispatch_queue_t)networkingQueue { - (void)stopAsync { _isRunning = false; + [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; } - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { @@ -229,7 +244,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { if (retryTime > now) { NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; - DSLog(@"Waiting %fl ms before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); + DSLog(@"[OBJ-C] CoinJoin: Waiting %fl ms before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); [self.inactives addObject:peerToTry]; [self triggerConnectionsJobWithDelay:delay]; return; diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 9a43662f8..87c3e472f 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -1826,7 +1826,7 @@ - (void)acceptCoinJoinFinalTransaction:(NSData *)message { } - (void)acceptCoinJoinQueueMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsq"); + DSLog(@"[OBJ-C] CoinJoin: got dsq from peer %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index f9c1e7e1f..ef44c70b8 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -49,8 +49,15 @@ - (void)startCoinJoin { _coinJoinManager = [DSCoinJoinManager sharedInstanceForChain:_chainManager.chain]; } - [_coinJoinManager startAsync]; - [_coinJoinManager runCoinJoin]; + [_coinJoinManager start]; +// wallet.getCoinJoin().refreshUnusedKeys(); TODO + [_coinJoinManager setStopOnNothingToDo:true]; + + if (![_coinJoinManager startMixing]) { + DSLog(@"[OBJ-C] CoinJoin: Mixing has been started already."); + } + + [_coinJoinManager doAutomaticDenominating]; } @end From 79846e1ec0b4b86d74e168fb4dd03102f8523649 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 28 Jul 2024 15:46:17 +0700 Subject: [PATCH 031/133] fix: reducing synchronizations --- .../Models/CoinJoin/DSCoinJoinManager.h | 5 +- .../Models/CoinJoin/DSCoinJoinManager.m | 74 ++++++++++++------ .../Models/CoinJoin/DSCoinJoinWrapper.h | 5 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 75 ++++++------------- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 40 +++------- 5 files changed, 90 insertions(+), 109 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index cb16df393..8ae02426d 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -35,8 +35,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @property (nonatomic, readonly) BOOL isWaitingForNewBlock; -@property (nonatomic, readonly) BOOL isMixing; @property (nonatomic, strong) id blocksObserver; +@property (atomic) BOOL isMixing; @property (atomic) BOOL isChainSynced; + (instancetype)sharedInstanceForChain:(DSChain *)chain; @@ -61,10 +61,11 @@ NS_ASSUME_NONNULL_BEGIN - (void)start; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; -- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; - (void)doAutomaticDenominating; +- (void)updateSuccessBlock; +- (BOOL)isWaitingForNewBlock; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 3a076de56..e331dc9a2 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -36,13 +36,15 @@ int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; +int32_t const MIN_BLOCKS_TO_WAIT = 1; @interface DSCoinJoinManager () -//@property (nonatomic, strong) dispatch_group_t processingGroup; @property (nonatomic, strong) dispatch_queue_t processingQueue; @property (nonatomic, strong) dispatch_source_t coinjoinTimer; @property (atomic) uint32_t lastSeenBlock; +@property (atomic) int32_t cachedLastSuccessBlock; +@property (atomic) int32_t cachedBlockHeight; // Keep track of current block height @end @@ -75,14 +77,15 @@ - (instancetype)initWithChain:(DSChain *)chain { _chain = chain; _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chain.chainManager]; _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; -// _processingGroup = dispatch_group_create(); _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.coinjoin.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); _lastSeenBlock = 0; + _cachedBlockHeight = 0; + _cachedLastSuccessBlock = 0; + _options = [self createOptions]; } return self; } - - (void)startAsync { if (!self.masternodeGroup.isRunning) { self.blocksObserver = @@ -92,7 +95,6 @@ - (void)startAsync { usingBlock:^(NSNotification *note) { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.isChainSynced = self.chain.chainManager.isSynced; - DSLog(@"[OBJ-C] CoinJoin: update isSynced: %s", self.isChainSynced ? "true" : "false"); dispatch_async(self.processingQueue, ^{ [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; }); @@ -113,7 +115,7 @@ - (void)start { uint32_t delay = 1; @synchronized (self) { - [self.wrapper registerCoinJoin]; + [self.wrapper registerCoinJoin:self.options]; self.coinjoinTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.processingQueue); if (self.coinjoinTimer) { dispatch_source_set_timer(self.coinjoinTimer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), interval * NSEC_PER_SEC, 1ull * NSEC_PER_SEC); @@ -127,26 +129,37 @@ - (void)start { } - (BOOL)startMixing { + self.isMixing = true; return [self.wrapper startMixing]; } +- (void)stop { + DSLog(@"[OBJ-C] CoinJoinManager stopping"); + [self cancelCoinjoinTimer]; + self.isMixing = false; +// [self.wrapper stopAndResetClientManager]; TODO + [self stopAsync]; +} + - (void)stopAsync { if (self.masternodeGroup != nil && self.masternodeGroup.isRunning) { - DSLog(@"[OBJ-C] CoinJoin: call stop"); + DSLog(@"[OBJ-C] CoinJoinManager stopAsync"); [self.chain.chainManager.peerManager shouldSendDsq:false]; [self.masternodeGroup stopAsync]; self.masternodeGroup = nil; - [self cancelCoinjoinTimer]; - [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; + } +} + +- (void)dealloc { + if (_options != NULL) { + free(_options); } } - (void)doAutomaticDenominating { dispatch_async(self.processingQueue, ^{ -// dispatch_group_enter(self.processingGroup); DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); [self.wrapper doAutomaticDenominating]; -// dispatch_group_leave(self.processingGroup); }); } @@ -165,15 +178,11 @@ - (void)setStopOnNothingToDo:(BOOL)stop { - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { dispatch_async(self.processingQueue, ^{ -// dispatch_group_enter(self.processingGroup); - if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { [self.wrapper processDSQueueFrom:peer message:message]; } else { [self.wrapper processMessageFrom:peer message:message type:type]; } - -// dispatch_group_leave(self.processingGroup); }); } @@ -577,9 +586,7 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output } dispatch_async(self.processingQueue, ^{ -// dispatch_group_enter(self.processingGroup); onPublished(error); -// dispatch_group_leave(self.processingGroup); }); }]; } @@ -627,20 +634,39 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with }]; } -- (BOOL)isWaitingForNewBlock { - return [self.wrapper isWaitingForNewBlock]; +- (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { + return [_masternodeGroup addPendingMasternode:proTxHash clientSessionId:sessionId]; } -- (BOOL)isMixing { - return [self.wrapper isMixing]; +- (void)updateSuccessBlock { + self.cachedLastSuccessBlock = self.cachedBlockHeight; } -- (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { - return [_masternodeGroup addPendingMasternode:proTxHash clientSessionId:sessionId]; +- (BOOL)isWaitingForNewBlock { + if (!self.isChainSynced) { + return true; + } + + if (self.options->coinjoin_multi_session == true) { + return false; + } + + return self.cachedBlockHeight - self.cachedLastSuccessBlock < MIN_BLOCKS_TO_WAIT; } -- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId { - return [_wrapper mixingMasternodeAddressFor:clientSessionId]; +- (CoinJoinClientOptions *)createOptions { + CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); + options->enable_coinjoin = YES; + options->coinjoin_rounds = 1; + options->coinjoin_sessions = 1; + options->coinjoin_amount = DUFFS / 4; // 0.25 DASH + options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; + options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; + options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; + options->coinjoin_multi_session = YES; + DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.chain.balance); + + return options; } @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index c1b3840e7..99d78dc40 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -31,20 +31,17 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, nullable) WalletEx *walletEx; @property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; -@property (nonatomic, assign, nullable) CoinJoinClientOptions *options; - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; -- (BOOL)isWaitingForNewBlock; - (BOOL)isMixing; - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message; -- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)notifyNewBestBlock:(DSBlock *)block; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; - (BOOL)doAutomaticDenominating; - (void)doMaintenance; -- (void)registerCoinJoin; +- (void)registerCoinJoin:(CoinJoinClientOptions *)options; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 5271d31cc..e7748f5d9 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -40,54 +40,23 @@ - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSCh return self; } -- (void)registerCoinJoin { +- (void)registerCoinJoin:(CoinJoinClientOptions *)options { @synchronized (self) { - if (_options == NULL) { - _options = [self createOptions]; - } - if (_walletEx == NULL) { DSLog(@"[OBJ-C] CoinJoin: register wallet ex"); - _walletEx = register_wallet_ex(AS_RUST(self), _options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync); + _walletEx = register_wallet_ex(AS_RUST(self), options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync); } if (_clientManager == NULL) { DSLog(@"[OBJ-C] CoinJoin: register client manager"); - _clientManager = register_client_manager(AS_RUST(self), _walletEx, _options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue); + _clientManager = register_client_manager(AS_RUST(self), _walletEx, options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock); DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); - add_client_queue_manager(_clientManager, _options, masternodeByHash, destroyMasternodeEntry, validMNCount, AS_RUST(self)); + add_client_queue_manager(_clientManager, masternodeByHash, destroyMasternodeEntry, validMNCount, AS_RUST(self)); } } } -- (BOOL)isWaitingForNewBlock { - @synchronized (self) { - return is_waiting_for_new_block(_clientManager); - } -} - -- (BOOL)isMixing { - @synchronized (self) { - return is_mixing(_clientManager); - } -} - -- (CoinJoinClientOptions *)createOptions { - CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); - options->enable_coinjoin = YES; - options->coinjoin_rounds = 1; - options->coinjoin_sessions = 1; - options->coinjoin_amount = DUFFS / 4; // 0.25 DASH - options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; - options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; - options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; - options->coinjoin_multi_session = YES; - DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.chainManager.chain.balance); - - return options; -} - - (void)setStopOnNothingToDo:(BOOL)stop { @synchronized (self) { set_stop_on_nothing_to_do(self.clientManager, stop); @@ -146,13 +115,11 @@ - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSStri process_coinjoin_message(_clientManager, peer.address.u8, peer.port, array, [type UTF8String]); - if (array) { - if (array->ptr) { - free((void *)array->ptr); - } - - free(array); + if (array->ptr) { + free((void *)array->ptr); } + + free(array); } } @@ -164,22 +131,12 @@ - (void)notifyNewBestBlock:(DSBlock *)block { } } -- (SocketAddress *)mixingMasternodeAddressFor:(UInt256)clientSessionId { - @synchronized (self) { - return mixing_masternode_address(_clientManager, (uint8_t (*)[32])(clientSessionId.u8)); - } -} - - (DSChain *)chain { return self.chainManager.chain; } - (void)dealloc { @synchronized (self) { - if (_options != NULL) { - free(_options); - } - unregister_client_manager(_clientManager); unregister_wallet_ex(_walletEx); // Unregister last } @@ -513,4 +470,20 @@ void startManagerAsync(const void *context) { } } +void updateSuccessBlock(const void *context) { + @synchronized (context) { + [AS_OBJC(context).manager updateSuccessBlock]; + } +} + +bool isWaitingForNewBlock(const void *context) { + BOOL result = NO; + + @synchronized (context) { + result = [AS_OBJC(context).manager isWaitingForNewBlock]; + } + + return result; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 1ab3f989c..b65fe0199 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -188,7 +188,6 @@ - (void)triggerConnections { - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)); dispatch_after(delayTime, self.networkingQueue, ^{ - DSLog(@"[OBJ-C] CoinJoin: triggerConnectionsJob"); if (!self.isRunning) { return; } @@ -244,9 +243,9 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; + NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; - if (retryTime > now) { - NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; + if (delay > 0) { DSLog(@"[OBJ-C] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); [self.inactives addObject:peerToTry]; [self triggerConnectionsJobWithDelay:delay]; @@ -271,31 +270,18 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { for (NSValue *sessionValue in self.pendingSessions) { UInt256 sessionId; [sessionValue getValue:&sessionId]; - DSSimplifiedMasternodeEntry *mixingMasternodeInfo1 = [self getMasternodeEntry:sessionId]; - - if (mixingMasternodeInfo1) { - DSLog(@"[OBJ-C] CoinJoin: mixingMasternodeInfo1 host: %@", mixingMasternodeInfo1.host); - UInt128 ipAddress = mixingMasternodeInfo1.address; //*((UInt128 *)mixingMasternodeInfo->ip_address); - uint16_t port = mixingMasternodeInfo1.port; //mixingMasternodeInfo->port; - } else { - DSLog(@"[OBJ-C] CoinJoin: mixingMasternodeInfo1 is nil"); - } - .// - SocketAddress *mixingMasternodeInfo = [self.coinJoinManager mixingMasternodeAddressFor:sessionId]; + DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; if (mixingMasternodeInfo) { - UInt128 ipAddress = *((UInt128 *)mixingMasternodeInfo->ip_address); - uint16_t port = mixingMasternodeInfo->port; + UInt128 ipAddress = mixingMasternodeInfo.address; + uint16_t port = mixingMasternodeInfo.port; DSPeer *peer = [self.chain.chainManager.peerManager peerForLocation:ipAddress port:port]; - DSLog(@"[OBJ-C] CoinJoin: mixingMasternodeInfo host: %@", peer); if (![self.pendingClosingMasternodes containsObject:peer]) { [addresses addObject:peer]; [self.addressMap setObject:sessionValue forKey:peer.location]; DSLog(@"[OBJ-C] CoinJoin: discovery: %@ -> %@", peer.location, uint256_hex(sessionId)); } - -// destroy_socket_address(mixingMasternodeInfo); } else { DSLog(@"[OBJ-C] CoinJoin: mixingMasternodeInfo is nil"); } @@ -305,17 +291,17 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { return [addresses copy]; } -- (DSSimplifiedMasternodeEntry *)getMasternodeEntry:(UInt256)sessionId { +- (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { for (NSValue* key in self.masternodeMap) { NSValue *object = [self.masternodeMap objectForKey:key]; UInt256 currentId = UINT256_ZERO; [object getValue:¤tId]; - if (uint128_eq(sessionId, currentId)) { + if (uint256_eq(sessionId, currentId)) { UInt256 proTxHash = UINT256_ZERO; [key getValue:&proTxHash]; - return [[self.chain.chainManager.masternodeManager currentMasternodeList] masternodeForRegistrationHash:proTxHash]; + return [self.coinJoinManager masternodeEntryByHash:proTxHash]; } } @@ -387,17 +373,15 @@ - (void)checkMasternodesWithoutSessions { for (NSValue *value in _pendingSessions) { UInt256 sessionId; [value getValue:&sessionId]; - SocketAddress *mixingMasternodeAddress = [_coinJoinManager mixingMasternodeAddressFor:sessionId]; + DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; if (mixingMasternodeAddress) { - UInt128 ipAddress = *((UInt128 *)mixingMasternodeAddress->ip_address); - uint16_t port = mixingMasternodeAddress->port; + UInt128 ipAddress = mixingMasternodeAddress.address; + uint16_t port = mixingMasternodeAddress.port; if (uint128_eq(ipAddress, peer.address) && port == peer.port) { found = YES; } - - destroy_socket_address(mixingMasternodeAddress); } else { // TODO(DashJ): we may not need this anymore DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); @@ -460,7 +444,7 @@ - (BOOL)connectTo:(DSPeer *)peer incrementMaxConnections:(BOOL)increment { return NO; } - SocketAddress *mixingMasternodeAddress = [_coinJoinManager mixingMasternodeAddressFor:sessionId]; + DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; if (!mixingMasternodeAddress) { DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, sessionId: %@", uint256_hex(sessionId)); From dfd36b32c9d02484f2cfdace0ddceb9cf8ebcbae Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 4 Aug 2024 14:43:10 +0700 Subject: [PATCH 032/133] chore: logs --- .../Models/CoinJoin/DSCoinJoinManager.h | 2 +- .../Models/CoinJoin/DSCoinJoinManager.m | 33 +- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 - .../Models/CoinJoin/DSCoinJoinWrapper.m | 3 - .../Models/CoinJoin/Utils/DSMasternodeGroup.h | 1 + .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 108 +-- DashSync/shared/Models/Network/DSPeer.m | 10 +- Example/DashSync.xcodeproj/project.pbxproj | 14 + Example/Podfile.lock | 732 ------------------ Example/Tests/CoinJoinTests.xctestplan | 60 ++ Example/Tests/CryptoTests.xctestplan | 1 + Example/Tests/DSCoinJoinSessionTest.m | 65 ++ Example/Tests/DerivationTests.xctestplan | 1 + Example/Tests/FullUnitTestPlan.xctestplan | 1 + Example/Tests/GovernanceTests.xctestplan | 1 + Example/Tests/LibraryTests.xctestplan | 1 + Example/Tests/LockTests.xctestplan | 1 + Example/Tests/MainnetSyncTests.xctestplan | 1 + Example/Tests/MasternodeListTests.xctestplan | 1 + Example/Tests/Metrics.xctestplan | 1 + Example/Tests/PaymentTests.xctestplan | 1 + .../Tests/PlatformTransitionTests.xctestplan | 1 + Example/Tests/TestnetE2ETests.xctestplan | 1 + Example/Tests/TestnetSyncTests.xctestplan | 1 + Example/Tests/TransactionTests.xctestplan | 1 + Example/Tests/WalletTests.xctestplan | 1 + 26 files changed, 251 insertions(+), 793 deletions(-) delete mode 100644 Example/Podfile.lock create mode 100644 Example/Tests/CoinJoinTests.xctestplan create mode 100644 Example/Tests/DSCoinJoinSessionTest.m diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 8ae02426d..bd6d62408 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -37,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) BOOL isWaitingForNewBlock; @property (nonatomic, strong) id blocksObserver; @property (atomic) BOOL isMixing; -@property (atomic) BOOL isChainSynced; +@property (readonly) BOOL isChainSynced; + (instancetype)sharedInstanceForChain:(DSChain *)chain; - (instancetype)initWithChain:(DSChain *)chain; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index e331dc9a2..ce0d4839f 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -33,6 +33,7 @@ #import "DSSimplifiedMasternodeEntry.h" #import "DSChain+Protected.h" #import "DSBlock.h" +#import "DashSync.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @@ -45,6 +46,7 @@ @interface DSCoinJoinManager () @property (atomic) uint32_t lastSeenBlock; @property (atomic) int32_t cachedLastSuccessBlock; @property (atomic) int32_t cachedBlockHeight; // Keep track of current block height +@property (atomic) BOOL isSynced; @end @@ -86,6 +88,14 @@ - (instancetype)initWithChain:(DSChain *)chain { return self; } +- (BOOL)isChainSynced { + if (!_isSynced) { + [[DashSync sharedSyncController] startSyncForChain:self.chain]; + } + + return _isSynced; +} + - (void)startAsync { if (!self.masternodeGroup.isRunning) { self.blocksObserver = @@ -94,7 +104,8 @@ - (void)startAsync { queue:nil usingBlock:^(NSNotification *note) { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { - self.isChainSynced = self.chain.chainManager.isSynced; + self.lastSeenBlock = self.chain.lastSyncBlock.height; + self.isSynced = self.chain.chainManager.isSynced; dispatch_async(self.processingQueue, ^{ [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; }); @@ -109,7 +120,7 @@ - (void)startAsync { - (void)start { DSLog(@"[OBJ-C] CoinJoinManager starting, time: %@", [NSDate date]); - self.isChainSynced = self.chain.chainManager.isSynced; + self.isSynced = self.chain.chainManager.isSynced; [self cancelCoinjoinTimer]; uint32_t interval = 1; uint32_t delay = 1; @@ -121,13 +132,25 @@ - (void)start { dispatch_source_set_timer(self.coinjoinTimer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), interval * NSEC_PER_SEC, 1ull * NSEC_PER_SEC); dispatch_source_set_event_handler(self.coinjoinTimer, ^{ DSLog(@"[OBJ-C] CoinJoin: trigger doMaintenance, time: %@", [NSDate date]); - [self.wrapper doMaintenance]; + [self doMaintenance]; }); dispatch_resume(self.coinjoinTimer); } } } +- (void)doMaintenance { + // TODO: + // report masternode group +// if (masternodeGroup != null) { +// tick++; +// if (tick % 15 == 0) { +// log.info(masternodeGroup.toString()); +// } +// } + [self.wrapper doMaintenance]; +} + - (BOOL)startMixing { self.isMixing = true; return [self.wrapper startMixing]; @@ -615,10 +638,12 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { } - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port warn:(BOOL)warn { - return [self.masternodeGroup forPeer:address port:port warn:warn withPredicate:^BOOL(DSPeer * _Nonnull peer) { + DSLog(@"[OBJ-C] CoinJoin peers: sendMessageOfType: %@ to %@", messageType, [self.masternodeGroup hostFor:address]); + return [self.masternodeGroup forPeer:address port:port warn:YES withPredicate:^BOOL(DSPeer * _Nonnull peer) { if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; [peer sendRequest:request]; + DSLog(@"[OBJ-C] CoinJoin dsa: sent"); } else if ([messageType isEqualToString:DSCoinJoinEntryMessage.type]) { DSCoinJoinEntryMessage *request = [DSCoinJoinEntryMessage requestWithData:message]; [peer sendRequest:request]; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 99d78dc40..d81b24f45 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -33,7 +33,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; -- (BOOL)isMixing; - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)notifyNewBestBlock:(DSBlock *)block; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index e7748f5d9..ca27e9e5e 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -148,7 +148,6 @@ - (void)dealloc { InputValue *getInputValueByPrevoutHash(uint8_t (*prevout_hash)[32], uint32_t index, const void *context) { UInt256 txHash = *((UInt256 *)prevout_hash); - DSLog(@"[OBJ-C CALLBACK] CoinJoin: getInputValueByPrevoutHash"); InputValue *inputValue = NULL; @synchronized (context) { @@ -171,7 +170,6 @@ - (void)dealloc { bool hasChainLock(Block *block, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: hasChainLock"); BOOL hasChainLock = NO; @synchronized (context) { @@ -201,7 +199,6 @@ bool hasChainLock(Block *block, const void *context) { } bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: isMine"); UInt256 txHash = *((UInt256 *)tx_hash); BOOL result = NO; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h index 1dcc43316..4aafa9ae2 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.h @@ -36,6 +36,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate; +- (NSString *)hostFor:(UInt128)address; @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index b65fe0199..1b613cb07 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -88,11 +88,12 @@ - (void)startAsync { queue:nil usingBlock:^(NSNotification *note) { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { - - DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); + self.lastSeenBlock = self.chain.lastSyncBlock.height; + DSLog(@"[OBJ-C] CoinJoin connect: new block found, restarting masternode connections job"); [self triggerConnections]; } }]; + [self triggerConnections]; } - (dispatch_queue_t)networkingQueue { @@ -121,8 +122,9 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { } - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { + DSLog(@"[OBJ-C] CoinJoin connect: disconnect mn: %@", [self hostFor:ip]); return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { - DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); + DSLog(@"[OBJ-C] CoinJoin connect: masternode[closing] %@", [self hostFor:ip]); @synchronized (self.pendingClosingMasternodes) { [self.pendingClosingMasternodes addObject:peer]; @@ -139,9 +141,12 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate { NSMutableString *listOfPeers = [NSMutableString string]; - for (DSPeer *peer in self.connectedPeers) { + NSSet *peers = self.connectedPeers; + DSLog(@"[OBJ-C] CoinJoin peers: forPeer, count: %lu", (unsigned long)peers.count); + + for (DSPeer *peer in peers) { [listOfPeers appendFormat:@"%@, ", peer.location]; - + if (uint128_eq(peer.address, ip) && peer.port == port) { return predicate(peer); } @@ -149,10 +154,10 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B if (warn) { if (![self isNodePending:ip port:port]) { - DSLog(@"[OBJ-C] CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); - NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); + DSLog(@"[OBJ-C] CoinJoin peers: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); +// NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); } else { - DSLog(@"[OBJ-C] CoinJoin: %@ in the list of pending peers: %@", [self hostFor:ip], listOfPeers); + DSLog(@"[OBJ-C] CoinJoin peers: %@ in the list of pending peers: %@", [self hostFor:ip], listOfPeers); } } @@ -209,7 +214,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { if (doDiscovery) { NSArray *peers = [self getPeers]; - for (DSPeer *peer in [self getPeers]) { + for (DSPeer *peer in peers) { [self addInactive:peer]; } @@ -252,7 +257,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { return; } - [self connectTo:peerToTry incrementMaxConnections:false]; + [self connectTo:peerToTry]; } NSUInteger count = self.mutablePendingPeers.count + self.connectedPeers.count; @@ -267,7 +272,9 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { NSMutableArray *addresses = [NSMutableArray array]; @synchronized(self.addressMap) { - for (NSValue *sessionValue in self.pendingSessions) { + NSArray *pendingSessionsCopy = [self.pendingSessions copy]; + + for (NSValue *sessionValue in pendingSessionsCopy) { UInt256 sessionId; [sessionValue getValue:&sessionId]; DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; @@ -280,10 +287,10 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { if (![self.pendingClosingMasternodes containsObject:peer]) { [addresses addObject:peer]; [self.addressMap setObject:sessionValue forKey:peer.location]; - DSLog(@"[OBJ-C] CoinJoin: discovery: %@ -> %@", peer.location, uint256_hex(sessionId)); + DSLog(@"[OBJ-C] CoinJoin peers: discovery: %@ -> %@", peer.location, uint256_hex(sessionId)); } } else { - DSLog(@"[OBJ-C] CoinJoin: mixingMasternodeInfo is nil"); + DSLog(@"[OBJ-C] CoinJoin peers: mixingMasternodeInfo is nil"); } } } @@ -309,13 +316,13 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { } - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { - @synchronized (_pendingSessions) { - DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash)); + @synchronized (self.pendingSessions) { + DSLog(@"[OBJ-C] CoinJoin connect: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; - [_pendingSessions addObject:sessionIdValue]; + [self.pendingSessions addObject:sessionIdValue]; NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; - [_masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; + [self.masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; [self updateMaxConnections]; [self checkMasternodesWithoutSessions]; @@ -325,9 +332,10 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session } - (void)updateMaxConnections { - _maxConnections = _pendingSessions.count; + DSLog(@"[OBJ-C] CoinJoin connect: updateMaxConnections, pendingSessions.count: %lu", self.pendingSessions.count); + _maxConnections = self.pendingSessions.count; NSUInteger connections = MIN(_maxConnections, DEFAULT_COINJOIN_SESSIONS); - DSLog(@"[OBJ-C] CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); + DSLog(@"[OBJ-C] CoinJoin connect: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); [self updateMaxConnections:connections]; } @@ -349,6 +357,7 @@ - (void)updateMaxConnections:(NSUInteger)connections { } if (adjustment > 0) { + DSLog(@"[OBJ-C] CoinJoin connect: triggerConnections for adjustment"); [self triggerConnections]; } @@ -370,7 +379,7 @@ - (void)checkMasternodesWithoutSessions { for (DSPeer *peer in self.connectedPeers) { BOOL found = false; - for (NSValue *value in _pendingSessions) { + for (NSValue *value in self.pendingSessions) { UInt256 sessionId; [value getValue:&sessionId]; DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; @@ -384,22 +393,22 @@ - (void)checkMasternodesWithoutSessions { } } else { // TODO(DashJ): we may not need this anymore - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); + DSLog(@"[OBJ-C] CoinJoin connect: session is not connected to a masternode: %@", uint256_hex(sessionId)); } } if (!found) { - DSLog(@"[OBJ-C] CoinJoin: masternode is not connected to a session: %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin connect: masternode is not connected to a session: %@", peer.location); [masternodesToDrop addObject:peer]; } } - DSLog(@"[OBJ-C] CoinJoin: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); + DSLog(@"[OBJ-C] CoinJoin connect: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; //pendingSessions.remove(mn.getProTxHash()); TODO: recheck (commented in DashJ) - DSLog(@"[OBJ-C] CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + DSLog(@"[OBJ-C] CoinJoin connect: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); [peer disconnect]; } } @@ -413,8 +422,8 @@ - (NSString *)hostFor:(UInt128)address { return @(inet_ntop(AF_INET6, &address, s, sizeof(s))); } -- (BOOL)connectTo:(DSPeer *)peer incrementMaxConnections:(BOOL)increment { - DSLog(@"[OBJ-C] CoinJoin: connectTo: %@", peer.location); +- (BOOL)connectTo:(DSPeer *)peer { + DSLog(@"[OBJ-C] CoinJoin peers: connectTo: %@", peer.location); if (![self isMasternodeSessionByPeer:peer]) { DSLog(@"[OBJ-C] CoinJoin: %@ not a masternode session, exit", peer.location); @@ -460,7 +469,8 @@ - (BOOL)connectTo:(DSPeer *)peer incrementMaxConnections:(BOOL)increment { [self.mutablePendingPeers addObject:peer]; } - DSLog(@"[OBJ-C] CoinJoin: calling peer.connect to %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin dsa: calling peer.connect to %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin connect: calling peer.connect to %@", peer.location); [peer connect]; return YES; @@ -495,7 +505,8 @@ - (void)peerConnected:(nonnull DSPeer *)peer { [_groupBackoff trackSuccess]; [[_backoffMap objectForKey:peer.location] trackSuccess]; - DSLog(@"[OBJ-C] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, _mutableConnectedPeers.count, _mutablePendingPeers.count, _maxConnections); + DSLog(@"[OBJ-C] CoinJoin dsa: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, _mutableConnectedPeers.count, _mutablePendingPeers.count, _maxConnections); + DSLog(@"[OBJ-C] CoinJoin connect: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, _mutableConnectedPeers.count, _mutablePendingPeers.count, _maxConnections); [_mutablePendingPeers removeObject:peer]; [_mutableConnectedPeers addObject:peer]; @@ -508,48 +519,49 @@ - (void)peerConnected:(nonnull DSPeer *)peer { - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { @synchronized (self) { - [_mutablePendingPeers removeObject:peer]; - [_mutableConnectedPeers removeObject:peer]; + [self.mutablePendingPeers removeObject:peer]; + [self.mutableConnectedPeers removeObject:peer]; - DSLog(@"[OBJ-C] CoinJoin: Peer died: %@ (%lu connected, %lu pending, %lu max)", peer.location, (unsigned long)_mutableConnectedPeers.count, (unsigned long)_mutablePendingPeers.count, (unsigned long)_maxConnections); + DSLog(@"[OBJ-C] CoinJoin peers: Peer died: %@ (%lu connected, %lu pending, %lu max)", peer.location, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); - [_groupBackoff trackFailure]; - [[_backoffMap objectForKey:peer.location] trackFailure]; + [self.groupBackoff trackFailure]; + [[self.backoffMap objectForKey:peer.location] trackFailure]; // Put back on inactive list [self addInactive:peer]; - NSUInteger numPeers = _mutablePendingPeers.count + _mutableConnectedPeers.count; - - if (numPeers < _maxConnections) { + NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; + + if (numPeers < self.maxConnections) { + DSLog(@"[OBJ-C] CoinJoin connect: triggerConnections to get to maxConnections"); [self triggerConnections]; } } - @synchronized (_pendingSessions) { + @synchronized (self.pendingSessions) { DSPeer *masternode = NULL; - for (DSPeer *mn in _pendingClosingMasternodes) { + for (DSPeer *mn in self.pendingClosingMasternodes) { if ([peer.location isEqualToString:mn.location]) { masternode = mn; } } - DSLog(@"[OBJ-C] CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"[OBJ-C] CoinJoin connect: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); if (masternode) { NSString *address = peer.location; - if ([_pendingClosingMasternodes containsObject:masternode]) { + if ([self.pendingClosingMasternodes containsObject:masternode]) { // if this is part of pendingClosingMasternodes, where we want to close the connection, // we don't want to increase the backoff time - [[_backoffMap objectForKey:address] trackSuccess]; + [[self.backoffMap objectForKey:address] trackSuccess]; } - [_pendingClosingMasternodes removeObject:masternode]; - UInt256 sessionId = [_chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; + [self.pendingClosingMasternodes removeObject:masternode]; + UInt256 sessionId = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; - [_pendingSessions removeObject:sessionIdValue]; - [_masternodeMap removeObjectForKey:sessionIdValue]; - [_addressMap removeObjectForKey:masternode.location]; + [self.pendingSessions removeObject:sessionIdValue]; + [self.masternodeMap removeObjectForKey:sessionIdValue]; + [self.addressMap removeObjectForKey:masternode.location]; } [self checkMasternodesWithoutSessions]; @@ -561,7 +573,9 @@ - (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { } - (void)addInactive:(DSPeer *)peer { - @synchronized (_inactives) { + DSLog(@"[OBJ-C] CoinJoin peers: addInactive: %@", peer.location); + + @synchronized (self.inactives) { // Deduplicate, handle differently than PeerGroup if ([self.inactives containsObject:peer]) { return; diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 80fa3ede9..1669b52e9 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -1822,27 +1822,27 @@ - (void)acceptGovObjectSyncMessage:(NSData *)message { // MARK: - Accept CoinJoin messages - (void)acceptCoinJoinCompleteMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsc"); + DSLog(@"[OBJ-C] CoinJoin: got dsc from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_COMPLETE]; } - (void)acceptCoinJoinFinalTransaction:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsf"); + DSLog(@"[OBJ-C] CoinJoin: got dsf from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_FINAL_TRANSACTION]; } - (void)acceptCoinJoinQueueMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsq from peer %@", self.location); + DSLog(@"[OBJ-C] CoinJoin: got dsq from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } - (void)acceptCoinJoinStatusUpdateMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dssu"); + DSLog(@"[OBJ-C] CoinJoin: got dssu from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_STATUS_UPDATE]; } - (void)acceptCoinJoinBroadcastTxMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dstx"); + DSLog(@"[OBJ-C] CoinJoin: got dstx from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_BROADCAST_TX]; } diff --git a/Example/DashSync.xcodeproj/project.pbxproj b/Example/DashSync.xcodeproj/project.pbxproj index b46854bc6..1d83b20f2 100644 --- a/Example/DashSync.xcodeproj/project.pbxproj +++ b/Example/DashSync.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; + 757323562C5A384800EF5FC0 /* DSCoinJoinSessionTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 757323542C5A381600EF5FC0 /* DSCoinJoinSessionTest.m */; }; 758719952B3ECB7A004C0E2D /* DSCoinJoinViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 758719942B3ECB7A004C0E2D /* DSCoinJoinViewController.m */; }; 798A60FA627EC43C0AB39611 /* libPods-DashSync-DashSync_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 192956F40996DE819B9D64AC /* libPods-DashSync-DashSync_Example.a */; }; 83CFCA70892A5DF9F64BA1AA /* libPods-DashSync_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 955D615511918752AF099181 /* libPods-DashSync_Tests.a */; }; @@ -617,6 +618,8 @@ 6675EA01A2AC0589160069A4 /* libPods-NetworkInfo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NetworkInfo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 74FDA0D6BCD2D779E20B58FC /* Pods-DashSync-DashSync_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DashSync-DashSync_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-DashSync-DashSync_Example/Pods-DashSync-DashSync_Example.release.xcconfig"; sourceTree = ""; }; + 757323532C5A329600EF5FC0 /* CoinJoinTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = CoinJoinTests.xctestplan; sourceTree = ""; }; + 757323542C5A381600EF5FC0 /* DSCoinJoinSessionTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCoinJoinSessionTest.m; sourceTree = ""; }; 758719932B3ECB28004C0E2D /* DSCoinJoinViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSCoinJoinViewController.h; sourceTree = ""; }; 758719942B3ECB7A004C0E2D /* DSCoinJoinViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSCoinJoinViewController.m; sourceTree = ""; }; 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -1443,6 +1446,7 @@ FBD45D1B24DF1A6200168EBC /* LibraryTests */, FBD45D1924DF1A0900168EBC /* L1Tests */, FB76C5A822BD7B9700DF2B29 /* MasternodeLists */, + 757323522C5A31ED00EF5FC0 /* CoinJoin */, FBE155FF22B99368009CA817 /* MasternodeList1088800.dat */, 2A5C601322BFC65E00C6003F /* ML_at_122088.dat */, 6003F5B6195388D20070C39A /* Supporting Files */, @@ -1473,6 +1477,14 @@ name = "Podspec Metadata"; sourceTree = ""; }; + 757323522C5A31ED00EF5FC0 /* CoinJoin */ = { + isa = PBXGroup; + children = ( + 757323542C5A381600EF5FC0 /* DSCoinJoinSessionTest.m */, + ); + name = CoinJoin; + sourceTree = ""; + }; 83DC6095687FDE098549FF30 /* Pods */ = { isa = PBXGroup; children = ( @@ -2139,6 +2151,7 @@ FB97500625B3876D00A1DCE7 /* TestnetSyncTests.xctestplan */, FB97501F25B3AD1400A1DCE7 /* Metrics.xctestplan */, FBBB464D25C054DE00ACDF35 /* TestnetE2ETests.xctestplan */, + 757323532C5A329600EF5FC0 /* CoinJoinTests.xctestplan */, ); name = TestPlans; sourceTree = ""; @@ -3051,6 +3064,7 @@ 2A1ECBD920D8B327000177D8 /* DSHashTests.m in Sources */, FB91B2D425325EB100511A44 /* MDCDamerauLevenshteinDistanceTests.m in Sources */, 2A7DCCB420DA198F0097049F /* DSPaymentProtocolTests.m in Sources */, + 757323562C5A384800EF5FC0 /* DSCoinJoinSessionTest.m in Sources */, FBAF79792385CA0200F4944E /* DSSparseMerkleTreeTests.m in Sources */, FB97501025B3888800A1DCE7 /* DSMainnetE2ETests.m in Sources */, FB97502B25B3BC2D00A1DCE7 /* DSMainnetSyncTests.m in Sources */, diff --git a/Example/Podfile.lock b/Example/Podfile.lock deleted file mode 100644 index d3c85e26e..000000000 --- a/Example/Podfile.lock +++ /dev/null @@ -1,732 +0,0 @@ -PODS: - - "!ProtoCompiler (3.21.5)": - - Protobuf (~> 3.0) - - "!ProtoCompiler-gRPCPlugin (1.49.0)": - - "!ProtoCompiler (= 3.21.5)" - - gRPC-ProtoRPC (= 1.49.0) - - abseil/algorithm/algorithm (1.20220623.0): - - abseil/base/config - - abseil/algorithm/container (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/base/atomic_hook (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/base (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/log_severity - - abseil/base/raw_logging_internal - - abseil/base/spinlock_wait - - abseil/meta/type_traits - - abseil/base/base_internal (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/base/config (1.20220623.0) - - abseil/base/core_headers (1.20220623.0): - - abseil/base/config - - abseil/base/dynamic_annotations (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver (1.20220623.0): - - abseil/base/config - - abseil/base/fast_type_id (1.20220623.0): - - abseil/base/config - - abseil/base/log_severity (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/malloc_internal (1.20220623.0): - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/base/prefetch (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/log_severity - - abseil/base/spinlock_wait (1.20220623.0): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/strerror (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/throw_delegate (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/container/common (1.20220623.0): - - abseil/meta/type_traits - - abseil/types/optional - - abseil/container/compressed_tuple (1.20220623.0): - - abseil/utility/utility - - abseil/container/container_memory (1.20220623.0): - - abseil/base/config - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/container/fixed_array (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/throw_delegate - - abseil/container/compressed_tuple - - abseil/memory/memory - - abseil/container/flat_hash_map (1.20220623.0): - - abseil/algorithm/container - - abseil/base/core_headers - - abseil/container/container_memory - - abseil/container/hash_function_defaults - - abseil/container/raw_hash_map - - abseil/memory/memory - - abseil/container/flat_hash_set (1.20220623.0): - - abseil/algorithm/container - - abseil/base/core_headers - - abseil/container/container_memory - - abseil/container/hash_function_defaults - - abseil/container/raw_hash_set - - abseil/memory/memory - - abseil/container/hash_function_defaults (1.20220623.0): - - abseil/base/config - - abseil/hash/hash - - abseil/strings/cord - - abseil/strings/strings - - abseil/container/hash_policy_traits (1.20220623.0): - - abseil/meta/type_traits - - abseil/container/hashtable_debug_hooks (1.20220623.0): - - abseil/base/config - - abseil/container/hashtablez_sampler (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/stacktrace - - abseil/memory/memory - - abseil/profiling/exponential_biased - - abseil/profiling/sample_recorder - - abseil/synchronization/synchronization - - abseil/utility/utility - - abseil/container/inlined_vector (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/throw_delegate - - abseil/container/inlined_vector_internal - - abseil/memory/memory - - abseil/container/inlined_vector_internal (1.20220623.0): - - abseil/base/core_headers - - abseil/container/compressed_tuple - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/types/span - - abseil/container/layout (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/types/span - - abseil/utility/utility - - abseil/container/raw_hash_map (1.20220623.0): - - abseil/base/throw_delegate - - abseil/container/container_memory - - abseil/container/raw_hash_set - - abseil/container/raw_hash_set (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/prefetch - - abseil/container/common - - abseil/container/compressed_tuple - - abseil/container/container_memory - - abseil/container/hash_policy_traits - - abseil/container/hashtable_debug_hooks - - abseil/container/hashtablez_sampler - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/utility/utility - - abseil/debugging/debugging_internal (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/errno_saver - - abseil/base/raw_logging_internal - - abseil/debugging/demangle_internal (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/stacktrace (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/debugging_internal - - abseil/debugging/symbolize (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/debugging/debugging_internal - - abseil/debugging/demangle_internal - - abseil/strings/strings - - abseil/functional/any_invocable (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/functional/bind_front (1.20220623.0): - - abseil/base/base_internal - - abseil/container/compressed_tuple - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/functional/function_ref (1.20220623.0): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/hash/city (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/hash/hash (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/container/fixed_array - - abseil/functional/function_ref - - abseil/hash/city - - abseil/hash/low_level_hash - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/strings/strings - - abseil/types/optional - - abseil/types/variant - - abseil/utility/utility - - abseil/hash/low_level_hash (1.20220623.0): - - abseil/base/config - - abseil/base/endian - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/memory/memory (1.20220623.0): - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/meta/type_traits (1.20220623.0): - - abseil/base/config - - abseil/numeric/bits (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/int128 (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/bits - - abseil/numeric/representation (1.20220623.0): - - abseil/base/config - - abseil/profiling/exponential_biased (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/profiling/sample_recorder (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/synchronization/synchronization - - abseil/time/time - - abseil/random/distributions (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/random/internal/distribution_caller - - abseil/random/internal/fast_uniform_bits - - abseil/random/internal/fastmath - - abseil/random/internal/generate_real - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/traits - - abseil/random/internal/uniform_helper - - abseil/random/internal/wide_multiply - - abseil/strings/strings - - abseil/random/internal/distribution_caller (1.20220623.0): - - abseil/base/config - - abseil/base/fast_type_id - - abseil/utility/utility - - abseil/random/internal/fast_uniform_bits (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/random/internal/traits - - abseil/random/internal/fastmath (1.20220623.0): - - abseil/numeric/bits - - abseil/random/internal/generate_real (1.20220623.0): - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/random/internal/fastmath - - abseil/random/internal/traits - - abseil/random/internal/iostream_state_saver (1.20220623.0): - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/random/internal/nonsecure_base (1.20220623.0): - - abseil/base/core_headers - - abseil/container/inlined_vector - - abseil/meta/type_traits - - abseil/random/internal/pool_urbg - - abseil/random/internal/salted_seed_seq - - abseil/random/internal/seed_material - - abseil/types/span - - abseil/random/internal/pcg_engine (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/fastmath - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/platform (1.20220623.0): - - abseil/base/config - - abseil/random/internal/pool_urbg (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/random/internal/randen - - abseil/random/internal/seed_material - - abseil/random/internal/traits - - abseil/random/seed_gen_exception - - abseil/types/span - - abseil/random/internal/randen (1.20220623.0): - - abseil/base/raw_logging_internal - - abseil/random/internal/platform - - abseil/random/internal/randen_hwaes - - abseil/random/internal/randen_slow - - abseil/random/internal/randen_engine (1.20220623.0): - - abseil/base/endian - - abseil/meta/type_traits - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/randen - - abseil/random/internal/randen_hwaes (1.20220623.0): - - abseil/base/config - - abseil/random/internal/platform - - abseil/random/internal/randen_hwaes_impl - - abseil/random/internal/randen_hwaes_impl (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/int128 - - abseil/random/internal/platform - - abseil/random/internal/randen_slow (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/numeric/int128 - - abseil/random/internal/platform - - abseil/random/internal/salted_seed_seq (1.20220623.0): - - abseil/container/inlined_vector - - abseil/meta/type_traits - - abseil/random/internal/seed_material - - abseil/types/optional - - abseil/types/span - - abseil/random/internal/seed_material (1.20220623.0): - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/random/internal/fast_uniform_bits - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/random/internal/traits (1.20220623.0): - - abseil/base/config - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/uniform_helper (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/random/internal/traits - - abseil/random/internal/wide_multiply (1.20220623.0): - - abseil/base/config - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/traits - - abseil/random/random (1.20220623.0): - - abseil/random/distributions - - abseil/random/internal/nonsecure_base - - abseil/random/internal/pcg_engine - - abseil/random/internal/pool_urbg - - abseil/random/internal/randen_engine - - abseil/random/seed_sequences - - abseil/random/seed_gen_exception (1.20220623.0): - - abseil/base/config - - abseil/random/seed_sequences (1.20220623.0): - - abseil/base/config - - abseil/random/internal/pool_urbg - - abseil/random/internal/salted_seed_seq - - abseil/random/internal/seed_material - - abseil/random/seed_gen_exception - - abseil/types/span - - abseil/status/status (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/base/strerror - - abseil/container/inlined_vector - - abseil/debugging/stacktrace - - abseil/debugging/symbolize - - abseil/functional/function_ref - - abseil/strings/cord - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/optional - - abseil/status/statusor (1.20220623.0): - - abseil/base/base - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/meta/type_traits - - abseil/status/status - - abseil/strings/strings - - abseil/types/variant - - abseil/utility/utility - - abseil/strings/cord (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/container/fixed_array - - abseil/container/inlined_vector - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/strings/cord_internal - - abseil/strings/cordz_functions - - abseil/strings/cordz_info - - abseil/strings/cordz_statistics - - abseil/strings/cordz_update_scope - - abseil/strings/cordz_update_tracker - - abseil/strings/internal - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/strings/cord_internal (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/base/throw_delegate - - abseil/container/compressed_tuple - - abseil/container/inlined_vector - - abseil/container/layout - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/types/span - - abseil/strings/cordz_functions (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/profiling/exponential_biased - - abseil/strings/cordz_handle (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/synchronization/synchronization - - abseil/strings/cordz_info (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/container/inlined_vector - - abseil/debugging/stacktrace - - abseil/strings/cord_internal - - abseil/strings/cordz_functions - - abseil/strings/cordz_handle - - abseil/strings/cordz_statistics - - abseil/strings/cordz_update_tracker - - abseil/synchronization/synchronization - - abseil/types/span - - abseil/strings/cordz_statistics (1.20220623.0): - - abseil/base/config - - abseil/strings/cordz_update_tracker - - abseil/strings/cordz_update_scope (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/strings/cord_internal - - abseil/strings/cordz_info - - abseil/strings/cordz_update_tracker - - abseil/strings/cordz_update_tracker (1.20220623.0): - - abseil/base/config - - abseil/strings/internal (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/meta/type_traits - - abseil/strings/str_format (1.20220623.0): - - abseil/strings/str_format_internal - - abseil/strings/str_format_internal (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/numeric/representation - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/utility/utility - - abseil/strings/strings (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/base/throw_delegate - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/strings/internal - - abseil/synchronization/graphcycles_internal (1.20220623.0): - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/synchronization/kernel_timeout_internal (1.20220623.0): - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/time/time - - abseil/synchronization/synchronization (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/debugging/stacktrace - - abseil/debugging/symbolize - - abseil/synchronization/graphcycles_internal - - abseil/synchronization/kernel_timeout_internal - - abseil/time/time - - abseil/time/internal/cctz/civil_time (1.20220623.0): - - abseil/base/config - - abseil/time/internal/cctz/time_zone (1.20220623.0): - - abseil/base/config - - abseil/time/internal/cctz/civil_time - - abseil/time/time (1.20220623.0): - - abseil/base/base - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/numeric/int128 - - abseil/strings/strings - - abseil/time/internal/cctz/civil_time - - abseil/time/internal/cctz/time_zone - - abseil/types/bad_optional_access (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/types/bad_variant_access (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/types/optional (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/types/bad_optional_access - - abseil/utility/utility - - abseil/types/span (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/throw_delegate - - abseil/meta/type_traits - - abseil/types/variant (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/types/bad_variant_access - - abseil/utility/utility - - abseil/utility/utility (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/meta/type_traits - - BoringSSL-GRPC (0.0.24): - - BoringSSL-GRPC/Implementation (= 0.0.24) - - BoringSSL-GRPC/Interface (= 0.0.24) - - BoringSSL-GRPC/Implementation (0.0.24): - - BoringSSL-GRPC/Interface (= 0.0.24) - - BoringSSL-GRPC/Interface (0.0.24) - - CocoaImageHashing (1.9.0) - - CocoaLumberjack (3.7.2): - - CocoaLumberjack/Core (= 3.7.2) - - CocoaLumberjack/Core (3.7.2) - - DAPI-GRPC (0.22.0-dev.8): - - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - - DAPI-GRPC/Messages (= 0.22.0-dev.8) - - DAPI-GRPC/Services (= 0.22.0-dev.8) - - DAPI-GRPC/Messages (0.22.0-dev.8): - - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - - Protobuf - - DAPI-GRPC/Services (0.22.0-dev.8): - - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - - DAPI-GRPC/Messages - - gRPC-ProtoRPC - - DashSharedCore (0.4.16) - - DashSync (0.1.0): - - CocoaLumberjack (= 3.7.2) - - DAPI-GRPC (= 0.22.0-dev.8) - - DSDynamicOptions (= 0.1.2) - - DWAlertController (= 0.2.1) - - TinyCborObjc (= 0.4.6) - - DSDynamicOptions (0.1.2) - - DWAlertController (0.2.1) - - gRPC-Core (1.49.0): - - gRPC-Core/Implementation (= 1.49.0) - - gRPC-Core/Interface (= 1.49.0) - - gRPC-Core/Implementation (1.49.0): - - abseil/base/base (= 1.20220623.0) - - abseil/base/core_headers (= 1.20220623.0) - - abseil/container/flat_hash_map (= 1.20220623.0) - - abseil/container/flat_hash_set (= 1.20220623.0) - - abseil/container/inlined_vector (= 1.20220623.0) - - abseil/functional/any_invocable (= 1.20220623.0) - - abseil/functional/bind_front (= 1.20220623.0) - - abseil/functional/function_ref (= 1.20220623.0) - - abseil/hash/hash (= 1.20220623.0) - - abseil/memory/memory (= 1.20220623.0) - - abseil/meta/type_traits (= 1.20220623.0) - - abseil/random/random (= 1.20220623.0) - - abseil/status/status (= 1.20220623.0) - - abseil/status/statusor (= 1.20220623.0) - - abseil/strings/cord (= 1.20220623.0) - - abseil/strings/str_format (= 1.20220623.0) - - abseil/strings/strings (= 1.20220623.0) - - abseil/synchronization/synchronization (= 1.20220623.0) - - abseil/time/time (= 1.20220623.0) - - abseil/types/optional (= 1.20220623.0) - - abseil/types/span (= 1.20220623.0) - - abseil/types/variant (= 1.20220623.0) - - abseil/utility/utility (= 1.20220623.0) - - BoringSSL-GRPC (= 0.0.24) - - gRPC-Core/Interface (= 1.49.0) - - gRPC-Core/Interface (1.49.0) - - gRPC-ProtoRPC (1.49.0): - - gRPC-ProtoRPC/Legacy (= 1.49.0) - - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) - - gRPC-ProtoRPC/Main (= 1.49.0) - - gRPC-ProtoRPC/Legacy (1.49.0): - - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) - - gRPC-ProtoRPC/Main (= 1.49.0) - - gRPC-RxLibrary (= 1.49.0) - - gRPC/GRPCCore (= 1.49.0) - - Protobuf (~> 3.0) - - gRPC-ProtoRPC/Legacy-Header (1.49.0) - - gRPC-ProtoRPC/Main (1.49.0): - - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) - - gRPC/Interface (= 1.49.0) - - Protobuf (~> 3.0) - - gRPC-RxLibrary (1.49.0): - - gRPC-RxLibrary/Implementation (= 1.49.0) - - gRPC-RxLibrary/Interface (= 1.49.0) - - gRPC-RxLibrary/Implementation (1.49.0): - - gRPC-RxLibrary/Interface - - gRPC-RxLibrary/Interface (1.49.0) - - gRPC/GRPCCore (1.49.0): - - gRPC-Core (= 1.49.0) - - gRPC-RxLibrary (= 1.49.0) - - gRPC/Interface (= 1.49.0) - - gRPC/Interface-Legacy (= 1.49.0) - - gRPC/Interface (1.49.0): - - gRPC/Interface-Legacy (= 1.49.0) - - gRPC/Interface-Legacy (1.49.0): - - gRPC-RxLibrary/Interface (= 1.49.0) - - KVO-MVVM (0.5.1) - - Protobuf (3.27.2) - - SDWebImage (5.14.3): - - SDWebImage/Core (= 5.14.3) - - SDWebImage/Core (5.14.3) - - tinycbor (0.5.4-alpha1) - - TinyCborObjc (0.4.6): - - tinycbor (= 0.5.4-alpha1) - -DEPENDENCIES: - - CocoaImageHashing (from `https://github.com/ameingast/cocoaimagehashing.git`, commit `ad01eee`) - - DashSharedCore (from `../../dash-shared-core/`) - - DashSync (from `../`) - - KVO-MVVM (= 0.5.1) - - SDWebImage (= 5.14.3) - -SPEC REPOS: - trunk: - - "!ProtoCompiler" - - "!ProtoCompiler-gRPCPlugin" - - abseil - - BoringSSL-GRPC - - CocoaLumberjack - - DAPI-GRPC - - DSDynamicOptions - - DWAlertController - - gRPC - - gRPC-Core - - gRPC-ProtoRPC - - gRPC-RxLibrary - - KVO-MVVM - - Protobuf - - SDWebImage - - tinycbor - - TinyCborObjc - -EXTERNAL SOURCES: - CocoaImageHashing: - :commit: ad01eee - :git: https://github.com/ameingast/cocoaimagehashing.git - DashSharedCore: - :path: "../../dash-shared-core/" - DashSync: - :path: "../" - -CHECKOUT OPTIONS: - CocoaImageHashing: - :commit: ad01eee - :git: https://github.com/ameingast/cocoaimagehashing.git - -SPEC CHECKSUMS: - "!ProtoCompiler": e9c09244955a8565817aa59a4787b6bb849a63c6 - "!ProtoCompiler-gRPCPlugin": 755f0ee414a0d5f0028e0dcfe98c23bdbc3e6fa3 - abseil: 926fb7a82dc6d2b8e1f2ed7f3a718bce691d1e46 - BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33 - CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 - CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da - DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b - DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 - DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc - DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b - gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f - gRPC-Core: 3a9fdb5967d42211e875826f3f6fc163ea02c2a1 - gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d - gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e - KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2 - SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 - tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 - TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 - -PODFILE CHECKSUM: faadd211f49d42e97495e9acfb4c5ac2f117c898 - -COCOAPODS: 1.15.2 diff --git a/Example/Tests/CoinJoinTests.xctestplan b/Example/Tests/CoinJoinTests.xctestplan new file mode 100644 index 000000000..5909fff17 --- /dev/null +++ b/Example/Tests/CoinJoinTests.xctestplan @@ -0,0 +1,60 @@ +{ + "configurations" : [ + { + "id" : "86CE06DB-48AE-4956-B3C5-C4AF38BA2644", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "testTimeoutsEnabled" : true + }, + "testTargets" : [ + { + "skippedTests" : [ + "DSAttackTests", + "DSBIP32Tests", + "DSBIP39Tests", + "DSBigNumberTests", + "DSBloomFilterTests", + "DSChainLockTests", + "DSChainTests", + "DSChainedSigningTests", + "DSDIP14Tests", + "DSDataTests", + "DSDeterministicMasternodeListTests", + "DSGovernanceTests", + "DSHashTests", + "DSIESEncryptedDataTests", + "DSInstantSendLockTests", + "DSInvitationsTests", + "DSKeyTests", + "DSMainnetE2ETests", + "DSMainnetMetricSyncTests", + "DSMainnetSyncTests", + "DSMiningTests", + "DSPaymentProtocolTests", + "DSPaymentRequestTests", + "DSProviderTransactionsTests", + "DSSparseMerkleTreeTests", + "DSTestnetE2ETests", + "DSTestnetMetricSyncTests", + "DSTestnetSyncTests", + "DSTransactionTests", + "DSTransitionTests", + "DSUInt256IndexPathTests", + "DSWalletTests", + "MDCDamerauLevenshteinDistanceTests", + "MDCLevenshteinDistanceTests" + ], + "target" : { + "containerPath" : "container:DashSync.xcodeproj", + "identifier" : "6003F5AD195388D20070C39A", + "name" : "DashSync_Tests" + } + } + ], + "version" : 1 +} diff --git a/Example/Tests/CryptoTests.xctestplan b/Example/Tests/CryptoTests.xctestplan index 844bc4278..ddd1bf30a 100644 --- a/Example/Tests/CryptoTests.xctestplan +++ b/Example/Tests/CryptoTests.xctestplan @@ -34,6 +34,7 @@ "DSBloomFilterTests", "DSChainLockTests", "DSChainTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/DSCoinJoinSessionTest.m b/Example/Tests/DSCoinJoinSessionTest.m new file mode 100644 index 000000000..ab4b8133a --- /dev/null +++ b/Example/Tests/DSCoinJoinSessionTest.m @@ -0,0 +1,65 @@ +// +// Created by Andrei Ashikhmin +// 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 + +@interface DSCoinJoinSessionTest : XCTestCase + +@end + +@implementation DSCoinJoinSessionTest + +- (void)sessionTest { + CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); + options->enable_coinjoin = YES; + options->coinjoin_rounds = 1; + options->coinjoin_sessions = 1; + options->coinjoin_amount = DUFFS / 4; // 0.25 DASH + options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; + options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; + options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; + options->coinjoin_multi_session = NO; + + + + + NSUInteger a = 7; + NSUInteger b = 5; + UInt256 bigA = uint256_from_long(a); + UInt256 bigB = uint256_from_long(b); + XCTAssert(uint256_sup(bigA, bigB), @"A in uint 256 needs to be bigger than B"); + + UInt256 bigC = ((UInt256){.u64 = {0, 1, 0, 0}}); + XCTAssert(uint256_sup(bigC, bigA), @"C in uint 256 needs to be bigger than A"); + + uint64_t d = 1 << 30; + UInt256 bigD = uint256_from_long(d); + UInt256 bigDLeftShifted = uInt256ShiftLeftLE(bigD, 34); + XCTAssert(uint256_eq(bigC, bigDLeftShifted), @"C and D should be equal"); + + uint32_t e = 1 << 30; + UInt256 bigE = uint256_from_int(e); + UInt256 bigELeftShifted = uInt256ShiftLeftLE(bigE, 34); + XCTAssert(uint256_eq(bigELeftShifted, bigDLeftShifted), @"D and E should be equal"); + + + free(options); +} + +@end diff --git a/Example/Tests/DerivationTests.xctestplan b/Example/Tests/DerivationTests.xctestplan index 24bbb3fa3..dc5494cc1 100644 --- a/Example/Tests/DerivationTests.xctestplan +++ b/Example/Tests/DerivationTests.xctestplan @@ -22,6 +22,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDataTests", "DSDeterministicMasternodeListTests", "DSGovernanceTests", diff --git a/Example/Tests/FullUnitTestPlan.xctestplan b/Example/Tests/FullUnitTestPlan.xctestplan index d33537564..d7bd388cc 100644 --- a/Example/Tests/FullUnitTestPlan.xctestplan +++ b/Example/Tests/FullUnitTestPlan.xctestplan @@ -15,6 +15,7 @@ { "skippedTests" : [ "DSAttackTests", + "DSCoinJoinSessionTest", "DSMainnetE2ETests", "DSMainnetMetricSyncTests", "DSMainnetSyncTests", diff --git a/Example/Tests/GovernanceTests.xctestplan b/Example/Tests/GovernanceTests.xctestplan index 474a6f8c2..e3219f0f3 100644 --- a/Example/Tests/GovernanceTests.xctestplan +++ b/Example/Tests/GovernanceTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/LibraryTests.xctestplan b/Example/Tests/LibraryTests.xctestplan index f0a38d13a..65cd295d7 100644 --- a/Example/Tests/LibraryTests.xctestplan +++ b/Example/Tests/LibraryTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDeterministicMasternodeListTests", diff --git a/Example/Tests/LockTests.xctestplan b/Example/Tests/LockTests.xctestplan index 02e33f8c8..bcf2495b2 100644 --- a/Example/Tests/LockTests.xctestplan +++ b/Example/Tests/LockTests.xctestplan @@ -22,6 +22,7 @@ "DSBloomFilterTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDataTests", "DSDeterministicMasternodeListTests", diff --git a/Example/Tests/MainnetSyncTests.xctestplan b/Example/Tests/MainnetSyncTests.xctestplan index 3c6f5f66c..338858f46 100644 --- a/Example/Tests/MainnetSyncTests.xctestplan +++ b/Example/Tests/MainnetSyncTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/MasternodeListTests.xctestplan b/Example/Tests/MasternodeListTests.xctestplan index e34fa0bca..6110205f0 100644 --- a/Example/Tests/MasternodeListTests.xctestplan +++ b/Example/Tests/MasternodeListTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/Metrics.xctestplan b/Example/Tests/Metrics.xctestplan index 9eb7df302..def05c053 100644 --- a/Example/Tests/Metrics.xctestplan +++ b/Example/Tests/Metrics.xctestplan @@ -24,6 +24,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/PaymentTests.xctestplan b/Example/Tests/PaymentTests.xctestplan index 0302c7fe8..09b32278d 100644 --- a/Example/Tests/PaymentTests.xctestplan +++ b/Example/Tests/PaymentTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/PlatformTransitionTests.xctestplan b/Example/Tests/PlatformTransitionTests.xctestplan index c28aac178..e0b432e03 100644 --- a/Example/Tests/PlatformTransitionTests.xctestplan +++ b/Example/Tests/PlatformTransitionTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/TestnetE2ETests.xctestplan b/Example/Tests/TestnetE2ETests.xctestplan index 5ebd633e2..4ef528e14 100644 --- a/Example/Tests/TestnetE2ETests.xctestplan +++ b/Example/Tests/TestnetE2ETests.xctestplan @@ -22,6 +22,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/TestnetSyncTests.xctestplan b/Example/Tests/TestnetSyncTests.xctestplan index 8cb7b4d7e..e9be7c98b 100644 --- a/Example/Tests/TestnetSyncTests.xctestplan +++ b/Example/Tests/TestnetSyncTests.xctestplan @@ -22,6 +22,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/TransactionTests.xctestplan b/Example/Tests/TransactionTests.xctestplan index 851737b7d..958d72896 100644 --- a/Example/Tests/TransactionTests.xctestplan +++ b/Example/Tests/TransactionTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", diff --git a/Example/Tests/WalletTests.xctestplan b/Example/Tests/WalletTests.xctestplan index 75a82735d..a7cdee10e 100644 --- a/Example/Tests/WalletTests.xctestplan +++ b/Example/Tests/WalletTests.xctestplan @@ -23,6 +23,7 @@ "DSChainLockTests", "DSChainTests", "DSChainedSigningTests", + "DSCoinJoinSessionTest", "DSDIP13Tests", "DSDIP14Tests", "DSDataTests", From f336045805ba575e94549728a1fc05b1d17cce44 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 6 Aug 2024 12:53:01 +0700 Subject: [PATCH 033/133] fix: dsa & dsi sending --- .../Models/CoinJoin/DSCoinJoinManager.m | 13 +- .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 - .../Models/CoinJoin/DSCoinJoinWrapper.m | 11 +- Example/Podfile.lock | 732 ++++++++++++++++++ 4 files changed, 741 insertions(+), 17 deletions(-) create mode 100644 Example/Podfile.lock diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index ce0d4839f..170c4a6e5 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -46,7 +46,6 @@ @interface DSCoinJoinManager () @property (atomic) uint32_t lastSeenBlock; @property (atomic) int32_t cachedLastSuccessBlock; @property (atomic) int32_t cachedBlockHeight; // Keep track of current block height -@property (atomic) BOOL isSynced; @end @@ -89,11 +88,13 @@ - (instancetype)initWithChain:(DSChain *)chain { } - (BOOL)isChainSynced { - if (!_isSynced) { - [[DashSync sharedSyncController] startSyncForChain:self.chain]; + BOOL isSynced = self.chain.chainManager.isSynced; + + if (!isSynced) { + [self.chain.chainManager startSync]; } - return _isSynced; + return isSynced; } - (void)startAsync { @@ -105,7 +106,6 @@ - (void)startAsync { usingBlock:^(NSNotification *note) { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.lastSeenBlock = self.chain.lastSyncBlock.height; - self.isSynced = self.chain.chainManager.isSynced; dispatch_async(self.processingQueue, ^{ [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; }); @@ -120,7 +120,6 @@ - (void)startAsync { - (void)start { DSLog(@"[OBJ-C] CoinJoinManager starting, time: %@", [NSDate date]); - self.isSynced = self.chain.chainManager.isSynced; [self cancelCoinjoinTimer]; uint32_t interval = 1; uint32_t delay = 1; @@ -490,7 +489,7 @@ - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { return false; } - if (!is_fully_mixed(_wrapper.walletEx, (uint8_t (*)[32])(utxo.hash.u8), (uint32_t)utxo.n)) { + if (!is_fully_mixed_with_manager(_wrapper.clientManager, (uint8_t (*)[32])(utxo.hash.u8), (uint32_t)utxo.n)) { return false; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index d81b24f45..832d09795 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -28,8 +28,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) DSChainManager *chainManager; @property (nonatomic, strong) DSChain *chain; @property (nonatomic, weak, nullable) DSCoinJoinManager *manager; - -@property (nonatomic, assign, nullable) WalletEx *walletEx; @property (nonatomic, assign, nullable) CoinJoinClientManager *clientManager; - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSChainManager *)chainManager; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index ca27e9e5e..8f6ce74ff 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -42,16 +42,12 @@ - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSCh - (void)registerCoinJoin:(CoinJoinClientOptions *)options { @synchronized (self) { - if (_walletEx == NULL) { - DSLog(@"[OBJ-C] CoinJoin: register wallet ex"); - _walletEx = register_wallet_ex(AS_RUST(self), options, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync); - } - if (_clientManager == NULL) { DSLog(@"[OBJ-C] CoinJoin: register client manager"); - _clientManager = register_client_manager(AS_RUST(self), _walletEx, options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock); - + _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync); + DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); + // TODO: add_wallet_ex add_client_queue_manager(_clientManager, masternodeByHash, destroyMasternodeEntry, validMNCount, AS_RUST(self)); } } @@ -138,7 +134,6 @@ - (DSChain *)chain { - (void)dealloc { @synchronized (self) { unregister_client_manager(_clientManager); - unregister_wallet_ex(_walletEx); // Unregister last } } diff --git a/Example/Podfile.lock b/Example/Podfile.lock new file mode 100644 index 000000000..d3c85e26e --- /dev/null +++ b/Example/Podfile.lock @@ -0,0 +1,732 @@ +PODS: + - "!ProtoCompiler (3.21.5)": + - Protobuf (~> 3.0) + - "!ProtoCompiler-gRPCPlugin (1.49.0)": + - "!ProtoCompiler (= 3.21.5)" + - gRPC-ProtoRPC (= 1.49.0) + - abseil/algorithm/algorithm (1.20220623.0): + - abseil/base/config + - abseil/algorithm/container (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/base/atomic_hook (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/base (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/base/base_internal (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/base/config (1.20220623.0) + - abseil/base/core_headers (1.20220623.0): + - abseil/base/config + - abseil/base/dynamic_annotations (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver (1.20220623.0): + - abseil/base/config + - abseil/base/fast_type_id (1.20220623.0): + - abseil/base/config + - abseil/base/log_severity (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal (1.20220623.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/base/prefetch (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/log_severity + - abseil/base/spinlock_wait (1.20220623.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/strerror (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/throw_delegate (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/container/common (1.20220623.0): + - abseil/meta/type_traits + - abseil/types/optional + - abseil/container/compressed_tuple (1.20220623.0): + - abseil/utility/utility + - abseil/container/container_memory (1.20220623.0): + - abseil/base/config + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/container/fixed_array (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/container/flat_hash_map (1.20220623.0): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_map + - abseil/memory/memory + - abseil/container/flat_hash_set (1.20220623.0): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_set + - abseil/memory/memory + - abseil/container/hash_function_defaults (1.20220623.0): + - abseil/base/config + - abseil/hash/hash + - abseil/strings/cord + - abseil/strings/strings + - abseil/container/hash_policy_traits (1.20220623.0): + - abseil/meta/type_traits + - abseil/container/hashtable_debug_hooks (1.20220623.0): + - abseil/base/config + - abseil/container/hashtablez_sampler (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/stacktrace + - abseil/memory/memory + - abseil/profiling/exponential_biased + - abseil/profiling/sample_recorder + - abseil/synchronization/synchronization + - abseil/utility/utility + - abseil/container/inlined_vector (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/container/inlined_vector_internal (1.20220623.0): + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/container/layout (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/utility/utility + - abseil/container/raw_hash_map (1.20220623.0): + - abseil/base/throw_delegate + - abseil/container/container_memory + - abseil/container/raw_hash_set + - abseil/container/raw_hash_set (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/prefetch + - abseil/container/common + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/hash_policy_traits + - abseil/container/hashtable_debug_hooks + - abseil/container/hashtablez_sampler + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/utility/utility + - abseil/debugging/debugging_internal (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/errno_saver + - abseil/base/raw_logging_internal + - abseil/debugging/demangle_internal (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/stacktrace (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/debugging_internal + - abseil/debugging/symbolize (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/debugging/demangle_internal + - abseil/strings/strings + - abseil/functional/any_invocable (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/functional/bind_front (1.20220623.0): + - abseil/base/base_internal + - abseil/container/compressed_tuple + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/functional/function_ref (1.20220623.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/hash/city (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/hash/hash (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/container/fixed_array + - abseil/functional/function_ref + - abseil/hash/city + - abseil/hash/low_level_hash + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/optional + - abseil/types/variant + - abseil/utility/utility + - abseil/hash/low_level_hash (1.20220623.0): + - abseil/base/config + - abseil/base/endian + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/memory/memory (1.20220623.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/meta/type_traits (1.20220623.0): + - abseil/base/config + - abseil/numeric/bits (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/int128 (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/bits + - abseil/numeric/representation (1.20220623.0): + - abseil/base/config + - abseil/profiling/exponential_biased (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/profiling/sample_recorder (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/random/distributions (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/distribution_caller + - abseil/random/internal/fast_uniform_bits + - abseil/random/internal/fastmath + - abseil/random/internal/generate_real + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/traits + - abseil/random/internal/uniform_helper + - abseil/random/internal/wide_multiply + - abseil/strings/strings + - abseil/random/internal/distribution_caller (1.20220623.0): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/utility/utility + - abseil/random/internal/fast_uniform_bits (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/random/internal/traits + - abseil/random/internal/fastmath (1.20220623.0): + - abseil/numeric/bits + - abseil/random/internal/generate_real (1.20220623.0): + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/fastmath + - abseil/random/internal/traits + - abseil/random/internal/iostream_state_saver (1.20220623.0): + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/random/internal/nonsecure_base (1.20220623.0): + - abseil/base/core_headers + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/types/span + - abseil/random/internal/pcg_engine (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/fastmath + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/platform (1.20220623.0): + - abseil/base/config + - abseil/random/internal/pool_urbg (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/random/internal/randen + - abseil/random/internal/seed_material + - abseil/random/internal/traits + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/random/internal/randen (1.20220623.0): + - abseil/base/raw_logging_internal + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes + - abseil/random/internal/randen_slow + - abseil/random/internal/randen_engine (1.20220623.0): + - abseil/base/endian + - abseil/meta/type_traits + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/randen + - abseil/random/internal/randen_hwaes (1.20220623.0): + - abseil/base/config + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes_impl + - abseil/random/internal/randen_hwaes_impl (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/random/internal/randen_slow (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/random/internal/salted_seed_seq (1.20220623.0): + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/seed_material + - abseil/types/optional + - abseil/types/span + - abseil/random/internal/seed_material (1.20220623.0): + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/random/internal/fast_uniform_bits + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/random/internal/traits (1.20220623.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/uniform_helper (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/random/internal/wide_multiply (1.20220623.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/random/random (1.20220623.0): + - abseil/random/distributions + - abseil/random/internal/nonsecure_base + - abseil/random/internal/pcg_engine + - abseil/random/internal/pool_urbg + - abseil/random/internal/randen_engine + - abseil/random/seed_sequences + - abseil/random/seed_gen_exception (1.20220623.0): + - abseil/base/config + - abseil/random/seed_sequences (1.20220623.0): + - abseil/base/config + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/status/status (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/base/strerror + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/functional/function_ref + - abseil/strings/cord + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/status/statusor (1.20220623.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/status/status + - abseil/strings/strings + - abseil/types/variant + - abseil/utility/utility + - abseil/strings/cord (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/container/fixed_array + - abseil/container/inlined_vector + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_info + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_scope + - abseil/strings/cordz_update_tracker + - abseil/strings/internal + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/strings/cord_internal (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/container/inlined_vector + - abseil/container/layout + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/strings/cordz_functions (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/profiling/exponential_biased + - abseil/strings/cordz_handle (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/synchronization/synchronization + - abseil/strings/cordz_info (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_handle + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_tracker + - abseil/synchronization/synchronization + - abseil/types/span + - abseil/strings/cordz_statistics (1.20220623.0): + - abseil/base/config + - abseil/strings/cordz_update_tracker + - abseil/strings/cordz_update_scope (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/cord_internal + - abseil/strings/cordz_info + - abseil/strings/cordz_update_tracker + - abseil/strings/cordz_update_tracker (1.20220623.0): + - abseil/base/config + - abseil/strings/internal (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/strings/str_format (1.20220623.0): + - abseil/strings/str_format_internal + - abseil/strings/str_format_internal (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/numeric/representation + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/utility/utility + - abseil/strings/strings (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/strings/internal + - abseil/synchronization/graphcycles_internal (1.20220623.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/synchronization/kernel_timeout_internal (1.20220623.0): + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/time/time + - abseil/synchronization/synchronization (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/synchronization/graphcycles_internal + - abseil/synchronization/kernel_timeout_internal + - abseil/time/time + - abseil/time/internal/cctz/civil_time (1.20220623.0): + - abseil/base/config + - abseil/time/internal/cctz/time_zone (1.20220623.0): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/time/time (1.20220623.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types/bad_optional_access (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_variant_access (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/optional (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/types/span (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/types/variant (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/utility/utility (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - BoringSSL-GRPC (0.0.24): + - BoringSSL-GRPC/Implementation (= 0.0.24) + - BoringSSL-GRPC/Interface (= 0.0.24) + - BoringSSL-GRPC/Implementation (0.0.24): + - BoringSSL-GRPC/Interface (= 0.0.24) + - BoringSSL-GRPC/Interface (0.0.24) + - CocoaImageHashing (1.9.0) + - CocoaLumberjack (3.7.2): + - CocoaLumberjack/Core (= 3.7.2) + - CocoaLumberjack/Core (3.7.2) + - DAPI-GRPC (0.22.0-dev.8): + - "!ProtoCompiler-gRPCPlugin (~> 1.0)" + - DAPI-GRPC/Messages (= 0.22.0-dev.8) + - DAPI-GRPC/Services (= 0.22.0-dev.8) + - DAPI-GRPC/Messages (0.22.0-dev.8): + - "!ProtoCompiler-gRPCPlugin (~> 1.0)" + - Protobuf + - DAPI-GRPC/Services (0.22.0-dev.8): + - "!ProtoCompiler-gRPCPlugin (~> 1.0)" + - DAPI-GRPC/Messages + - gRPC-ProtoRPC + - DashSharedCore (0.4.16) + - DashSync (0.1.0): + - CocoaLumberjack (= 3.7.2) + - DAPI-GRPC (= 0.22.0-dev.8) + - DSDynamicOptions (= 0.1.2) + - DWAlertController (= 0.2.1) + - TinyCborObjc (= 0.4.6) + - DSDynamicOptions (0.1.2) + - DWAlertController (0.2.1) + - gRPC-Core (1.49.0): + - gRPC-Core/Implementation (= 1.49.0) + - gRPC-Core/Interface (= 1.49.0) + - gRPC-Core/Implementation (1.49.0): + - abseil/base/base (= 1.20220623.0) + - abseil/base/core_headers (= 1.20220623.0) + - abseil/container/flat_hash_map (= 1.20220623.0) + - abseil/container/flat_hash_set (= 1.20220623.0) + - abseil/container/inlined_vector (= 1.20220623.0) + - abseil/functional/any_invocable (= 1.20220623.0) + - abseil/functional/bind_front (= 1.20220623.0) + - abseil/functional/function_ref (= 1.20220623.0) + - abseil/hash/hash (= 1.20220623.0) + - abseil/memory/memory (= 1.20220623.0) + - abseil/meta/type_traits (= 1.20220623.0) + - abseil/random/random (= 1.20220623.0) + - abseil/status/status (= 1.20220623.0) + - abseil/status/statusor (= 1.20220623.0) + - abseil/strings/cord (= 1.20220623.0) + - abseil/strings/str_format (= 1.20220623.0) + - abseil/strings/strings (= 1.20220623.0) + - abseil/synchronization/synchronization (= 1.20220623.0) + - abseil/time/time (= 1.20220623.0) + - abseil/types/optional (= 1.20220623.0) + - abseil/types/span (= 1.20220623.0) + - abseil/types/variant (= 1.20220623.0) + - abseil/utility/utility (= 1.20220623.0) + - BoringSSL-GRPC (= 0.0.24) + - gRPC-Core/Interface (= 1.49.0) + - gRPC-Core/Interface (1.49.0) + - gRPC-ProtoRPC (1.49.0): + - gRPC-ProtoRPC/Legacy (= 1.49.0) + - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) + - gRPC-ProtoRPC/Main (= 1.49.0) + - gRPC-ProtoRPC/Legacy (1.49.0): + - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) + - gRPC-ProtoRPC/Main (= 1.49.0) + - gRPC-RxLibrary (= 1.49.0) + - gRPC/GRPCCore (= 1.49.0) + - Protobuf (~> 3.0) + - gRPC-ProtoRPC/Legacy-Header (1.49.0) + - gRPC-ProtoRPC/Main (1.49.0): + - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) + - gRPC/Interface (= 1.49.0) + - Protobuf (~> 3.0) + - gRPC-RxLibrary (1.49.0): + - gRPC-RxLibrary/Implementation (= 1.49.0) + - gRPC-RxLibrary/Interface (= 1.49.0) + - gRPC-RxLibrary/Implementation (1.49.0): + - gRPC-RxLibrary/Interface + - gRPC-RxLibrary/Interface (1.49.0) + - gRPC/GRPCCore (1.49.0): + - gRPC-Core (= 1.49.0) + - gRPC-RxLibrary (= 1.49.0) + - gRPC/Interface (= 1.49.0) + - gRPC/Interface-Legacy (= 1.49.0) + - gRPC/Interface (1.49.0): + - gRPC/Interface-Legacy (= 1.49.0) + - gRPC/Interface-Legacy (1.49.0): + - gRPC-RxLibrary/Interface (= 1.49.0) + - KVO-MVVM (0.5.1) + - Protobuf (3.27.2) + - SDWebImage (5.14.3): + - SDWebImage/Core (= 5.14.3) + - SDWebImage/Core (5.14.3) + - tinycbor (0.5.4-alpha1) + - TinyCborObjc (0.4.6): + - tinycbor (= 0.5.4-alpha1) + +DEPENDENCIES: + - CocoaImageHashing (from `https://github.com/ameingast/cocoaimagehashing.git`, commit `ad01eee`) + - DashSharedCore (from `../../dash-shared-core/`) + - DashSync (from `../`) + - KVO-MVVM (= 0.5.1) + - SDWebImage (= 5.14.3) + +SPEC REPOS: + trunk: + - "!ProtoCompiler" + - "!ProtoCompiler-gRPCPlugin" + - abseil + - BoringSSL-GRPC + - CocoaLumberjack + - DAPI-GRPC + - DSDynamicOptions + - DWAlertController + - gRPC + - gRPC-Core + - gRPC-ProtoRPC + - gRPC-RxLibrary + - KVO-MVVM + - Protobuf + - SDWebImage + - tinycbor + - TinyCborObjc + +EXTERNAL SOURCES: + CocoaImageHashing: + :commit: ad01eee + :git: https://github.com/ameingast/cocoaimagehashing.git + DashSharedCore: + :path: "../../dash-shared-core/" + DashSync: + :path: "../" + +CHECKOUT OPTIONS: + CocoaImageHashing: + :commit: ad01eee + :git: https://github.com/ameingast/cocoaimagehashing.git + +SPEC CHECKSUMS: + "!ProtoCompiler": e9c09244955a8565817aa59a4787b6bb849a63c6 + "!ProtoCompiler-gRPCPlugin": 755f0ee414a0d5f0028e0dcfe98c23bdbc3e6fa3 + abseil: 926fb7a82dc6d2b8e1f2ed7f3a718bce691d1e46 + BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33 + CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 + CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da + DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f + DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b + DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 + DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc + DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b + gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f + gRPC-Core: 3a9fdb5967d42211e875826f3f6fc163ea02c2a1 + gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d + gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e + KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 + Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2 + SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 + tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 + TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 + +PODFILE CHECKSUM: faadd211f49d42e97495e9acfb4c5ac2f117c898 + +COCOAPODS: 1.15.2 From 13573d9027371ff4160eefe9b6521875c6cdc303 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 6 Aug 2024 12:56:12 +0700 Subject: [PATCH 034/133] chore: cleanup --- .../Models/CoinJoin/DSCoinJoinManager.m | 1 - Example/Tests/DSCoinJoinSessionTest.m | 23 +------------------ 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 170c4a6e5..9c72e8896 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -33,7 +33,6 @@ #import "DSSimplifiedMasternodeEntry.h" #import "DSChain+Protected.h" #import "DSBlock.h" -#import "DashSync.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; diff --git a/Example/Tests/DSCoinJoinSessionTest.m b/Example/Tests/DSCoinJoinSessionTest.m index ab4b8133a..b1b26878b 100644 --- a/Example/Tests/DSCoinJoinSessionTest.m +++ b/Example/Tests/DSCoinJoinSessionTest.m @@ -36,28 +36,7 @@ - (void)sessionTest { options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; options->coinjoin_multi_session = NO; - - - - NSUInteger a = 7; - NSUInteger b = 5; - UInt256 bigA = uint256_from_long(a); - UInt256 bigB = uint256_from_long(b); - XCTAssert(uint256_sup(bigA, bigB), @"A in uint 256 needs to be bigger than B"); - - UInt256 bigC = ((UInt256){.u64 = {0, 1, 0, 0}}); - XCTAssert(uint256_sup(bigC, bigA), @"C in uint 256 needs to be bigger than A"); - - uint64_t d = 1 << 30; - UInt256 bigD = uint256_from_long(d); - UInt256 bigDLeftShifted = uInt256ShiftLeftLE(bigD, 34); - XCTAssert(uint256_eq(bigC, bigDLeftShifted), @"C and D should be equal"); - - uint32_t e = 1 << 30; - UInt256 bigE = uint256_from_int(e); - UInt256 bigELeftShifted = uInt256ShiftLeftLE(bigE, 34); - XCTAssert(uint256_eq(bigELeftShifted, bigDLeftShifted), @"D and E should be equal"); - + // TODO: session test free(options); } From 3fb40132bcdebb8ec2fa1741281100a56defdb38 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 13 Aug 2024 11:28:07 +0700 Subject: [PATCH 035/133] fix: anyonecanpay & masternode group improvements --- .../Models/CoinJoin/DSCoinJoinWrapper.m | 6 +- .../Models/CoinJoin/DSTransaction+CoinJoin.m | 7 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 118 ++++++++++++++---- .../Models/Transactions/Base/DSTransaction.h | 2 + .../Models/Transactions/Base/DSTransaction.m | 55 ++++---- DashSync/shared/Models/Wallet/DSAccount.h | 12 ++ DashSync/shared/Models/Wallet/DSAccount.m | 12 +- 7 files changed, 162 insertions(+), 50 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 8f6ce74ff..1b7732bcb 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -292,15 +292,14 @@ void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { free(gatheredOutputs); } -Transaction* signTransaction(Transaction *transaction, const void *context) { +Transaction* signTransaction(Transaction *transaction, bool anyoneCanPay, const void *context) { DSLog(@"[OBJ-C CALLBACK] CoinJoin: signTransaction"); @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; destroy_transaction(transaction); - - BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx]; + BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx anyoneCanPay:anyoneCanPay]; if (isSigned) { return [tx ffi_malloc:wrapper.chain.chainType]; @@ -390,7 +389,6 @@ uint64_t validMNCount(const void *context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSMasternodeList *mnList = [wrapper.manager mnList]; // TODO: might have 0 valid MNs, account for this - DSLog(@"[OBJ-C] CoinJoin: getMNList, valid count: %llu", mnList.validMasternodeCount); masternodes = [mnList ffi_malloc]; } diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m index c4e6b6f32..d5ef22415 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -36,13 +36,16 @@ - (DSTransaction *)initWithTransaction:(Transaction *)transaction onChain:(DSCha UInt256 hashValue; memcpy(hashValue.u8, *input->input_hash, 32); NSNumber *index = @(input->index); - NSData *script; + NSData *script = [NSData data]; if (input->script && input->script_length != 0) { script = [NSData dataWithBytes:input->script length:input->script_length]; } else { DSTransaction *inputTx = [chain transactionForHash:hashValue]; - script = [inputTx.outputs objectAtIndex:index.integerValue].outScript; + + if (inputTx) { + script = [inputTx.outputs objectAtIndex:index.integerValue].outScript; + } } NSNumber *sequence = @(input->sequence); diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 1b613cb07..7e80d2908 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -25,6 +25,7 @@ #import "DSSendCoinJoinQueue.h" #import "DSBackoff.h" #import "DSBlock.h" +#import "DSPeerManager+Protected.h" #import float_t const MIN_PEER_DISCOVERY_INTERVAL = 1; // One second @@ -155,7 +156,7 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B if (warn) { if (![self isNodePending:ip port:port]) { DSLog(@"[OBJ-C] CoinJoin peers: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); -// NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); +// NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); TODO } else { DSLog(@"[OBJ-C] CoinJoin peers: %@ in the list of pending peers: %@", [self hostFor:ip], listOfPeers); } @@ -206,8 +207,9 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized (self.inactives) { BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; + DSLog(@"[OBJ-C] CoinJoin connect: havPeersToTry: %s", havPeersToTry ? "YES" : "NO"); doDiscovery = !havPeersToTry; - NSUInteger numPeers = self.mutablePendingPeers.count + self.connectedPeers.count; + NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; DSPeer *peerToTry = nil; NSDate *retryTime = nil; @@ -233,7 +235,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { if (numPeers < self.maxConnections) { NSTimeInterval interval = MAX([self.groupBackoff.retryTime timeIntervalSinceDate:now], MIN_PEER_DISCOVERY_INTERVAL); - DSLog(@"[OBJ-C] CoinJoin: Masternode discovery didn't provide us any more masternodes, will try again in %fl ms.", interval); + DSLog(@"[OBJ-C] CoinJoin: Masternode discovery didn't provide us any more masternodes, will try again in %fl ms. MaxConnections: %lu", interval, (unsigned long)self.maxConnections); [self triggerConnectionsJobWithDelay:interval]; } else { @@ -242,17 +244,33 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } return; } else { + NSMutableString *backoffs = [NSMutableString string]; + + for (DSPeer *peer in self.inactives) { + DSBackoff *backoff = [self.backoffMap objectForKey:peer.location]; + [backoffs appendFormat:@"[%@ : %@], ", peer.location, backoff.retryTime]; + } + + DSLog(@"[OBJ-C] CoinJoin: backoffs for inactives before peerToTry: %@", backoffs); + peerToTry = self.inactives.firstObject; [self.inactives removeObjectAtIndex:0]; retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; } - retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; + DSLog(@"[OBJ-C] CoinJoin connect: retry time for: %@ is %@", peerToTry.location, retryTime); + + if (numPeers > 0) { + retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; + } + + DSLog(@"[OBJ-C] CoinJoin connect: final retry time for: %@ is %@", peerToTry.location, retryTime); NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; if (delay > 0) { DSLog(@"[OBJ-C] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); [self.inactives addObject:peerToTry]; + [self sortInactives]; [self triggerConnectionsJobWithDelay:delay]; return; } @@ -260,7 +278,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { [self connectTo:peerToTry]; } - NSUInteger count = self.mutablePendingPeers.count + self.connectedPeers.count; + NSUInteger count = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; if (count < self.maxConnections) { [self triggerConnectionsJobWithDelay:0]; // Try next peer immediately. @@ -282,7 +300,33 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { if (mixingMasternodeInfo) { UInt128 ipAddress = mixingMasternodeInfo.address; uint16_t port = mixingMasternodeInfo.port; - DSPeer *peer = [self.chain.chainManager.peerManager peerForLocation:ipAddress port:port]; + DSPeer *peer = [self peerForLocation:ipAddress port:port]; + + if (peer == nil) { + BOOL inPendingClosing = NO; + BOOL inConnected = NO; + BOOL inPending = NO; + BOOL inInactives = NO; + + if ([self.pendingClosingMasternodes containsObject:peer]) { + inPendingClosing = true; + } + + if ([self.connectedPeers containsObject:peer]) { + inConnected = true; + } + + if ([self.inactives containsObject:peer]) { + inInactives = true; + } + + if ([self.pendingPeers containsObject:peer]) { + inPending = true; + } + + DSLog(@"[OBJ-C] CoinJoin peers: not found for %@, inPendingClosing: %s, inConnectedPeers: %s, inInactives: %s, inPending: %s, creating new", [self hostFor:ipAddress], inPendingClosing ? "YES" : "NO", inConnected ? "YES" : "NO", inInactives ? "YES" : "NO", inPending ? "YES" : "NO"); + peer = [DSPeer peerWithAddress:ipAddress andPort:port onChain:self.chain]; + } if (![self.pendingClosingMasternodes containsObject:peer]) { [addresses addObject:peer]; @@ -348,12 +392,15 @@ - (void)updateMaxConnections:(NSUInteger)connections { } // We may now have too many or too few open connections. Add more or drop some to get to the right amount. - NSUInteger adjustment = 0; + NSInteger adjustment = 0; NSSet *connectedPeers = self.connectedPeers; @synchronized (self.mutablePendingPeers) { - NSUInteger numPeers = _mutablePendingPeers.count + connectedPeers.count; - adjustment = _maxConnections - numPeers; + NSUInteger pendingCount = self.mutablePendingPeers.count; + NSUInteger connectedCount = connectedPeers.count; + NSUInteger numPeers = pendingCount + connectedCount; + adjustment = self.maxConnections - numPeers; + DSLog(@"[OBJ-C] CoinJoin connect: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); } if (adjustment > 0) { @@ -469,8 +516,7 @@ - (BOOL)connectTo:(DSPeer *)peer { [self.mutablePendingPeers addObject:peer]; } - DSLog(@"[OBJ-C] CoinJoin dsa: calling peer.connect to %@", peer.location); - DSLog(@"[OBJ-C] CoinJoin connect: calling peer.connect to %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin: calling peer.connect to %@", peer.location); [peer connect]; return YES; @@ -502,22 +548,23 @@ - (BOOL)isNodePending:(DSPeer *)node { - (void)peerConnected:(nonnull DSPeer *)peer { @synchronized (self) { - [_groupBackoff trackSuccess]; - [[_backoffMap objectForKey:peer.location] trackSuccess]; + [self.groupBackoff trackSuccess]; + [[self.backoffMap objectForKey:peer.location] trackSuccess]; - DSLog(@"[OBJ-C] CoinJoin dsa: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, _mutableConnectedPeers.count, _mutablePendingPeers.count, _maxConnections); - DSLog(@"[OBJ-C] CoinJoin connect: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, _mutableConnectedPeers.count, _mutablePendingPeers.count, _maxConnections); + DSLog(@"[OBJ-C] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); - [_mutablePendingPeers removeObject:peer]; - [_mutableConnectedPeers addObject:peer]; + [self.mutablePendingPeers removeObject:peer]; + [self.mutableConnectedPeers addObject:peer]; } - if (_shouldSendDsq) { + if (self.shouldSendDsq) { [peer sendRequest:[DSSendCoinJoinQueue requestWithShouldSend:true]]; } } - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { + DSLog(@"[OBJ-C] CoinJoin connect: %@ disconnectedWithError %@", peer.location, error); + @synchronized (self) { [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; @@ -573,33 +620,62 @@ - (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { } - (void)addInactive:(DSPeer *)peer { - DSLog(@"[OBJ-C] CoinJoin peers: addInactive: %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin peers: addInactive: %@, currentCount: %lu, isConnected: %s, isConnecting: %s", peer.location, (unsigned long)self.inactives.count, peer.status == DSPeerStatus_Connected ? "YES" : "NO", peer.status == DSPeerStatus_Connecting ? "YES" : "NO"); @synchronized (self.inactives) { // Deduplicate, handle differently than PeerGroup if ([self.inactives containsObject:peer]) { return; } - + // do not connect to another the same peer twice if ([self isNodeConnected:peer]) { DSLog(@"[OBJ-C] CoinJoin: attempting to connect to the same masternode again: %@", peer.location); return; } - DSBackoff *backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:GROUP_BACKOFF_MULTIPLIER]; + DSBackoff *backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:BACKOFF_MULTIPLIER]; [self.backoffMap setObject:backoff forKey:peer.location]; [self.inactives addObject:peer]; [self sortInactives]; } } +- (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { + for (DSPeer *peer in self.connectedPeers) { + if (uint128_eq(peer.address, ipAddress) && peer.port == port) { + DSLog(@"[OBJ-C] CoinJoin connect: peerForLocation found in connectedPeers"); + return peer; + } + } + + for (DSPeer *peer in self.pendingPeers) { + if (uint128_eq(peer.address, ipAddress) && peer.port == port) { + DSLog(@"[OBJ-C] CoinJoin connect: peerForLocation found in pendingPeers"); + return peer; + } + } + + return [self.chain.chainManager.peerManager peerForLocation:ipAddress port:port]; +} + - (void)sortInactives { [_inactives sortUsingComparator:^NSComparisonResult(DSPeer *obj1, DSPeer *obj2) { DSBackoff *backoff1 = [_backoffMap objectForKey:obj1.location]; DSBackoff *backoff2 = [_backoffMap objectForKey:obj2.location]; return [backoff1.retryTime compare:backoff2.retryTime]; }]; + + if (_inactives.count > 1) { + NSMutableString *backoffs = [NSMutableString string]; + + for (DSPeer *peer in _inactives) { + DSBackoff *backoff = [_backoffMap objectForKey:peer.location]; + [backoffs appendFormat:@"[%@ : %@], ", peer.location, backoff.retryTime]; + } + + DSLog(@"[OBJ-C] CoinJoin: backoffs after sorting: %@", backoffs); + } } @end diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.h b/DashSync/shared/Models/Transactions/Base/DSTransaction.h index 036fba696..5f808a845 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.h +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.h @@ -48,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN #define TX_LOCKTIME 0x00000000u #define TXIN_SEQUENCE UINT32_MAX #define SIGHASH_ALL 0x00000001u +#define SIGHASH_ANYONECANPAY 0x80u #define MAX_ECDSA_SIGNATURE_SIZE 75 @@ -144,6 +145,7 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) - (void)hasSetInputsAndOutputs; - (BOOL)signWithSerializedPrivateKeys:(NSArray *)privateKeys; - (BOOL)signWithPrivateKeys:(NSArray *)keys; +- (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay; - (BOOL)signWithPreorderedPrivateKeys:(NSArray *)keys; - (NSString *_Nullable)shapeshiftOutboundAddress; diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index 8af718a59..d9e928f98 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -584,6 +584,10 @@ - (BOOL)signWithSerializedPrivateKeys:(NSArray *)privateKeys { } - (BOOL)signWithPrivateKeys:(NSArray *)keys { + return [self signWithPrivateKeys:keys anyoneCanPay:NO]; +} + +- (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { NSMutableArray *addresses = [NSMutableArray arrayWithCapacity:keys.count]; // TODO: avoid double looping: defer getting address into signWithPrivateKeys key <-> address @@ -592,27 +596,36 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys { } @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]; - 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]]; - } - - transactionInput.signature = sig; + DSTransactionInput *transactionInput = self.mInputs[i]; + NSString *addr = [DSKeyManager addressWithScriptPubKey:transactionInput.inScript forChain:self.chain]; + NSUInteger keyIdx = (addr) ? [addresses indexOfObject:addr] : NSNotFound; + if (keyIdx == NSNotFound) { + if (anyoneCanPay && !transactionInput.signature) { + transactionInput.signature = [NSData data]; + } + + 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]; + uint8_t sighashFlags = SIGHASH_ALL; + if (anyoneCanPay) { + sighashFlags |= SIGHASH_ANYONECANPAY; + DSLog(@"[OBJ-C] CoinJoin: private key data: %@", [DSKeyManager privateKeyData:key].hexString); + } + [s appendUInt8:sighashFlags]; + [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]]; + } + + transactionInput.signature = sig; } if (!self.isSigned) return NO; diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index 381b8796c..2d16b37cf 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -190,6 +190,18 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT /// - (BOOL)signTransaction:(DSTransaction *)transaction; +/// Sign any inputs in the given transaction that can be signed using private keys from the wallet +/// +/// - Parameters: +/// - transaction: Instance of `DSTransaction` you want to sign +/// - anyoneCanPay: apply SIGHASH_ANYONECANPAY signature type +/// +/// - Returns: boolean value indicating if the transaction was signed +/// +/// - Note: Using this method to sign a tx doesn't present pin controller, use this method carefully from UI +/// +- (BOOL)signTransaction:(DSTransaction *)transaction anyoneCanPay:(BOOL)anyoneCanPay; + /// Sign any inputs in the given transaction that can be signed using private keys from the wallet /// /// - Parameters: diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 980465da3..69541fe15 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1296,6 +1296,10 @@ - (NSArray *)usedDerivationPathsForTransaction:(DSTransaction *)transaction { } - (BOOL)signTransaction:(DSTransaction *)transaction { + return [self signTransaction:transaction anyoneCanPay:NO]; +} + +- (BOOL)signTransaction:(DSTransaction *)transaction anyoneCanPay:(BOOL)anyoneCanPay { NSParameterAssert(transaction); if (_isViewOnlyAccount) return NO; @@ -1308,7 +1312,7 @@ - (BOOL)signTransaction:(DSTransaction *)transaction { if (!seed) { return NO; } else { - return [self signTxWithDerivationPaths:usedDerivationPaths tx:transaction seed:seed]; + return [self signTxWithDerivationPaths:usedDerivationPaths tx:transaction seed:seed anyoneCanPay:anyoneCanPay]; } } } @@ -1335,6 +1339,10 @@ - (void)signTransaction:(DSTransaction *)transaction withPrompt:(NSString *_Null } - (BOOL)signTxWithDerivationPaths:(NSArray *)usedDerivationPaths tx:(DSTransaction *)tx seed:(NSData *)seed { + return [self signTxWithDerivationPaths:usedDerivationPaths tx:tx seed:seed anyoneCanPay:NO]; +} + +- (BOOL)signTxWithDerivationPaths:(NSArray *)usedDerivationPaths tx:(DSTransaction *)tx seed:(NSData *)seed anyoneCanPay:(BOOL)anyoneCanPay { NSMutableArray *privkeys = [NSMutableArray array]; for (NSDictionary *dictionary in usedDerivationPaths) { DSDerivationPath *derivationPath = dictionary[@"derivationPath"]; @@ -1352,7 +1360,7 @@ - (BOOL)signTxWithDerivationPaths:(NSArray *)usedDerivationPaths tx:(DSTransacti } } - return [tx signWithPrivateKeys:privkeys]; + return [tx signWithPrivateKeys:privkeys anyoneCanPay:anyoneCanPay]; } // sign any inputs in the given transaction that can be signed using private keys from the wallet From 53b534417e34677b5a420b0ea738829fdedefe8e Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 19 Aug 2024 11:53:37 +0700 Subject: [PATCH 036/133] chore: fixes --- .../Models/CoinJoin/DSCoinJoinManager.h | 3 +- .../Models/CoinJoin/DSCoinJoinManager.m | 66 ++++++++++++------- .../Models/CoinJoin/DSCoinJoinWrapper.m | 24 +++++-- .../Models/CoinJoin/DSTransaction+CoinJoin.m | 2 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 66 ++++++++----------- DashSync/shared/Models/Network/DSPeer.m | 3 +- .../Models/Transactions/Base/DSTransaction.m | 18 +++-- DashSync/shared/Models/Wallet/DSAccount.m | 3 +- Example/DashSync/Wallets.storyboard | 6 +- Example/Tests/DSCoinJoinSessionTest.m | 2 +- 10 files changed, 115 insertions(+), 78 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index bd6d62408..10e2b1c98 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -35,7 +35,6 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @property (nonatomic, readonly) BOOL isWaitingForNewBlock; -@property (nonatomic, strong) id blocksObserver; @property (atomic) BOOL isMixing; @property (readonly) BOOL isChainSynced; @@ -47,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount; - (NSString *)freshAddress:(BOOL)internal; -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished; +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished; - (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; - (uint64_t)validMNCount; - (DSMasternodeList *)mnList; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 9c72e8896..76e07e189 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -90,6 +90,7 @@ - (BOOL)isChainSynced { BOOL isSynced = self.chain.chainManager.isSynced; if (!isSynced) { + DSLog(@"[OBJ-C] CoinJoin: combinedSyncProgress: %f", self.chain.chainManager.syncState.combinedSyncProgress); [self.chain.chainManager startSync]; } @@ -98,18 +99,14 @@ - (BOOL)isChainSynced { - (void)startAsync { if (!self.masternodeGroup.isRunning) { - self.blocksObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:DSChainManagerSyncStateDidChangeNotification - object:nil - queue:nil - usingBlock:^(NSNotification *note) { - if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { - self.lastSeenBlock = self.chain.lastSyncBlock.height; - dispatch_async(self.processingQueue, ^{ - [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; - }); - } - }]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleSyncStateDidChangeNotification:) + name:DSChainManagerSyncStateDidChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleTransactionReceivedNotification) + name:DSTransactionManagerTransactionReceivedNotification + object:nil]; DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); [self.chain.chainManager.peerManager shouldSendDsq:true]; @@ -129,7 +126,6 @@ - (void)start { if (self.coinjoinTimer) { dispatch_source_set_timer(self.coinjoinTimer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), interval * NSEC_PER_SEC, 1ull * NSEC_PER_SEC); dispatch_source_set_event_handler(self.coinjoinTimer, ^{ - DSLog(@"[OBJ-C] CoinJoin: trigger doMaintenance, time: %@", [NSDate date]); [self doMaintenance]; }); dispatch_resume(self.coinjoinTimer); @@ -146,6 +142,12 @@ - (void)doMaintenance { // log.info(masternodeGroup.toString()); // } // } + + if ([self validMNCount] == 0) { + DSLog(@"[OBJ-C] CoinJoin doMaintenance: No Masternodes detected."); + return; + } + [self.wrapper doMaintenance]; } @@ -169,6 +171,8 @@ - (void)stopAsync { [self.masternodeGroup stopAsync]; self.masternodeGroup = nil; } + + [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)dealloc { @@ -177,7 +181,25 @@ - (void)dealloc { } } +- (void)handleSyncStateDidChangeNotification:(NSNotification *)note { + if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { + self.lastSeenBlock = self.chain.lastSyncBlock.height; + dispatch_async(self.processingQueue, ^{ + [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; + }); + } +} + +- (void)handleTransactionReceivedNotification { + +} + - (void)doAutomaticDenominating { + if ([self validMNCount] == 0) { + DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); + return; + } + dispatch_async(self.processingQueue, ^{ DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); [self.wrapper doAutomaticDenominating]; @@ -267,7 +289,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { continue; } - if (![account containsAddress:output.address]) { // TODO: is it the same as isPubKeyMine? + if (![account containsAddress:output.address]) { continue; } @@ -317,7 +339,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { // construct resulting vector // NOTE: vecTallyRet is "sorted" by txdest (i.e. address), just like mapTally - NSMutableArray *vecTallyRet = [[NSMutableArray alloc] init]; + NSMutableArray *vecTallyRet = [NSMutableArray array]; for (DSCompactTallyItem *item in mapTally.allValues) { // TODO: (dashj) ignore this to get this dust back in @@ -375,8 +397,9 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { BOOL allowUsedAddresses = /* !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || */ (coinControl != nil && !coinControl.avoidAddressReuse); int32_t minDepth = coinControl != nil ? coinControl.minDepth : DEFAULT_MIN_DEPTH; int32_t maxDepth = coinControl != nil ? coinControl.maxDepth : DEFAULT_MAX_DEPTH; + NSSet *spendables = [self getSpendableTXs]; - for (DSTransaction *coin in [self getSpendableTXs]) { + for (DSTransaction *coin in spendables) { UInt256 wtxid = coin.txHash; DSAccount *account = [self.chain firstAccountThatCanContainTransaction:coin]; @@ -525,8 +548,6 @@ - (Balance *)getBalance { } } - DSLog(@"[OBJ-C] CoinJoin: denominatedBalance: %llu", denominatedBalance); - Balance *balance = malloc(sizeof(Balance)); balance->my_trusted = self.chain.chainManager.chain.balance; balance->denominated_trusted = denominatedBalance; @@ -585,7 +606,7 @@ - (NSString *)freshAddress:(BOOL)internal { return address; } -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(NSError * _Nullable error))onPublished { +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished { DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES]; @@ -607,7 +628,7 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output } dispatch_async(self.processingQueue, ^{ - onPublished(error); + onPublished(transaction.txHash, error); }); }]; } @@ -641,13 +662,14 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; [peer sendRequest:request]; - DSLog(@"[OBJ-C] CoinJoin dsa: sent"); } else if ([messageType isEqualToString:DSCoinJoinEntryMessage.type]) { DSCoinJoinEntryMessage *request = [DSCoinJoinEntryMessage requestWithData:message]; [peer sendRequest:request]; + DSLog(@"[OBJ-C] CoinJoin dsi: sent"); } else if ([messageType isEqualToString:DSCoinJoinSignedInputs.type]) { DSCoinJoinSignedInputs *request = [DSCoinJoinSignedInputs requestWithData:message]; [peer sendRequest:request]; + DSLog(@"[OBJ-C] CoinJoin dss: sent"); } else { DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); return NO; @@ -682,7 +704,7 @@ - (CoinJoinClientOptions *)createOptions { options->enable_coinjoin = YES; options->coinjoin_rounds = 1; options->coinjoin_sessions = 1; - options->coinjoin_amount = DUFFS / 4; // 0.25 DASH + options->coinjoin_amount = DUFFS / 8; // 0.125 DASH options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 1b7732bcb..65484839a 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -44,7 +44,7 @@ - (void)registerCoinJoin:(CoinJoinClientOptions *)options { @synchronized (self) { if (_clientManager == NULL) { DSLog(@"[OBJ-C] CoinJoin: register client manager"); - _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync); + _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionCompleteListener); DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); // TODO: add_wallet_ex @@ -338,14 +338,17 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_d @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); - result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts onPublished:^(NSError * _Nullable error) { + result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts onPublished:^(UInt256 txId, NSError * _Nullable error) { if (error) { - DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@", error); + DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); } else if (is_denominating) { - DSLog(@"[OBJ-C] CoinJoin: call finish_automatic_denominating"); + DSLog(@"[OBJ-C] CoinJoin: Denominations Created: %@", uint256_reverse_hex(txId)); bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); processor_destroy_block_hash(client_session_id); DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); + } else { + DSLog(@"[OBJ-C] CoinJoin: Collateral Created: %@", uint256_reverse_hex(txId)); + // TODO: call listeners } }]; } @@ -388,7 +391,6 @@ uint64_t validMNCount(const void *context) { @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSMasternodeList *mnList = [wrapper.manager mnList]; - // TODO: might have 0 valid MNs, account for this masternodes = [mnList ffi_malloc]; } @@ -476,4 +478,16 @@ bool isWaitingForNewBlock(const void *context) { return result; } +void sessionCompleteListener(bool is_complete, + int32_t base_session_id, + uint8_t (*client_session_id)[32], + uint32_t denomination, + enum PoolState state, + enum PoolMessage message, + uint8_t (*ip_address)[16], + bool joined, + const void *context) { + // TODO +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m index d5ef22415..946948d3a 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -44,7 +44,7 @@ - (DSTransaction *)initWithTransaction:(Transaction *)transaction onChain:(DSCha DSTransaction *inputTx = [chain transactionForHash:hashValue]; if (inputTx) { - script = [inputTx.outputs objectAtIndex:index.integerValue].outScript; + script = inputTx.outputs[index.integerValue].outScript; } } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 7e80d2908..c8cae9d46 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -52,7 +52,6 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) NSMutableDictionary *backoffMap; @property (nonatomic, strong) NSMutableArray *inactives; @property (nonatomic, strong) NSLock *lock; -@property (nonatomic, strong) id blocksObserver; @property (nonatomic) uint32_t lastSeenBlock; @end @@ -83,17 +82,10 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { - (void)startAsync { _isRunning = true; - self.blocksObserver = - [[NSNotificationCenter defaultCenter] addObserverForName:DSChainManagerSyncStateDidChangeNotification - object:nil - queue:nil - usingBlock:^(NSNotification *note) { - if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { - self.lastSeenBlock = self.chain.lastSyncBlock.height; - DSLog(@"[OBJ-C] CoinJoin connect: new block found, restarting masternode connections job"); - [self triggerConnections]; - } - }]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleSyncStateDidChangeNotification:) + name:DSChainManagerSyncStateDidChangeNotification + object:nil]; [self triggerConnections]; } @@ -103,7 +95,7 @@ - (dispatch_queue_t)networkingQueue { - (void)stopAsync { _isRunning = false; - [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { @@ -143,7 +135,6 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B NSMutableString *listOfPeers = [NSMutableString string]; NSSet *peers = self.connectedPeers; - DSLog(@"[OBJ-C] CoinJoin peers: forPeer, count: %lu", (unsigned long)peers.count); for (DSPeer *peer in peers) { [listOfPeers appendFormat:@"%@, ", peer.location]; @@ -291,40 +282,22 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized(self.addressMap) { NSArray *pendingSessionsCopy = [self.pendingSessions copy]; + DSLog(@"[OBJ-C] CoinJoin getPeers, pendingSessions len: %lu", (unsigned long)pendingSessionsCopy.count); for (NSValue *sessionValue in pendingSessionsCopy) { UInt256 sessionId; [sessionValue getValue:&sessionId]; + DSLog(@"[OBJ-C] CoinJoin getPeers, sessionId: %@", uint256_hex(sessionId)); DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; if (mixingMasternodeInfo) { UInt128 ipAddress = mixingMasternodeInfo.address; + DSLog(@"[OBJ-C] CoinJoin getPeers, ipAddress: %@", [self hostFor:ipAddress]); uint16_t port = mixingMasternodeInfo.port; DSPeer *peer = [self peerForLocation:ipAddress port:port]; if (peer == nil) { - BOOL inPendingClosing = NO; - BOOL inConnected = NO; - BOOL inPending = NO; - BOOL inInactives = NO; - - if ([self.pendingClosingMasternodes containsObject:peer]) { - inPendingClosing = true; - } - - if ([self.connectedPeers containsObject:peer]) { - inConnected = true; - } - - if ([self.inactives containsObject:peer]) { - inInactives = true; - } - - if ([self.pendingPeers containsObject:peer]) { - inPending = true; - } - - DSLog(@"[OBJ-C] CoinJoin peers: not found for %@, inPendingClosing: %s, inConnectedPeers: %s, inInactives: %s, inPending: %s, creating new", [self hostFor:ipAddress], inPendingClosing ? "YES" : "NO", inConnected ? "YES" : "NO", inInactives ? "YES" : "NO", inPending ? "YES" : "NO"); + DSLog(@"[OBJ-C] CoinJoin peers: not found for %@, creating new", [self hostFor:ipAddress]); peer = [DSPeer peerWithAddress:ipAddress andPort:port onChain:self.chain]; } @@ -368,6 +341,18 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; [self.masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; + // ~~~~~~~~~ + UInt128 ipAddress = UINT128_ZERO; + DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; + + if (mixingMasternodeInfo) { + ipAddress = mixingMasternodeInfo.address; + } + + DSLog(@"[OBJ-C] CoinJoin connect: adding masternode for mixing. location: %@", [self hostFor:ipAddress]); + + // ~~~~~~~~~~ + [self updateMaxConnections]; [self checkMasternodesWithoutSessions]; } @@ -454,7 +439,6 @@ - (void)checkMasternodesWithoutSessions { for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; - //pendingSessions.remove(mn.getProTxHash()); TODO: recheck (commented in DashJ) DSLog(@"[OBJ-C] CoinJoin connect: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); [peer disconnect]; } @@ -678,4 +662,12 @@ - (void)sortInactives { } } +- (void)handleSyncStateDidChangeNotification:(NSNotification *)note { + if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { + self.lastSeenBlock = self.chain.lastSyncBlock.height; + DSLog(@"[OBJ-C] CoinJoin connect: new block found, restarting masternode connections job"); + [self triggerConnections]; + } +} + @end diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 1669b52e9..c213d65ff 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -378,7 +378,7 @@ - (void)receivedOrphanBlock { - (void)sendRequest:(DSMessageRequest *)request { NSString *type = [request type]; NSData *payload = [request toData]; - //DSLog(@"%@:%u sendRequest: [%@]: %@", self.host, self.port, type, [payload hexString]); + DSLog(@"%@:%u sendRequest: [%@]: %@", self.host, self.port, type, [payload hexString]); [self sendMessage:payload type:type]; } @@ -1832,7 +1832,6 @@ - (void)acceptCoinJoinFinalTransaction:(NSData *)message { } - (void)acceptCoinJoinQueueMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsq from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index d9e928f98..f2d9ba580 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -416,6 +416,10 @@ - (NSData *)toData { // Returns the binary transaction data that needs to be hashed and signed with the private key for the tx input at // subscriptIndex. A subscriptIndex of NSNotFound will return the entire signed transaction. - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { + return [self toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:NO]; +} + +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { NSArray *inputs = self.inputs; NSArray *outputs = self.outputs; @@ -426,7 +430,7 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { NSMutableData *d = [NSMutableData dataWithCapacity:dataSize]; [d appendUInt16:self.version]; - [d appendUInt16:self.type]; +// [d appendUInt16:self.type]; [d appendVarInt:inputsCount]; for (NSUInteger i = 0; i < inputsCount; i++) { @@ -454,7 +458,14 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { } [d appendUInt32:self.lockTime]; - if (forSigHash) [d appendUInt32:SIGHASH_ALL]; + if (forSigHash) { + uint8_t sighashFlags = SIGHASH_ALL; + if (anyoneCanPay) { + sighashFlags |= SIGHASH_ANYONECANPAY; + } + + [d appendUInt32:sighashFlags]; + } return [d copy]; } } @@ -606,7 +617,7 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { continue; } - NSData *data = [self toDataWithSubscriptIndex:i]; + NSData *data = [self toDataWithSubscriptIndex:i anyoneCanPay:anyoneCanPay]; NSMutableData *sig = [NSMutableData data]; NSValue *keyValue = keys[keyIdx]; OpaqueKey *key = ((OpaqueKey *) keyValue.pointerValue); @@ -616,7 +627,6 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { uint8_t sighashFlags = SIGHASH_ALL; if (anyoneCanPay) { sighashFlags |= SIGHASH_ANYONECANPAY; - DSLog(@"[OBJ-C] CoinJoin: private key data: %@", [DSKeyManager privateKeyData:key].hexString); } [s appendUInt8:sighashFlags]; [sig appendScriptPushData:s]; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 69541fe15..078f26e7c 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1502,7 +1502,8 @@ - (BOOL)transactionIsValid:(DSTransaction *)transaction { @synchronized (self) { if (transaction.blockHeight != TX_UNCONFIRMED) return YES; if (self.allTx[uint256_obj(transaction.txHash)] != nil) { - return ![self.invalidTransactionHashes containsObject:uint256_obj(transaction.txHash)]; + BOOL invalid = [self.invalidTransactionHashes containsObject:uint256_obj(transaction.txHash)]; + return !invalid; } for (DSTransactionInput *input in transaction.inputs) { UInt256 h = input.inputHash; diff --git a/Example/DashSync/Wallets.storyboard b/Example/DashSync/Wallets.storyboard index 56f7d7582..b738666a8 100644 --- a/Example/DashSync/Wallets.storyboard +++ b/Example/DashSync/Wallets.storyboard @@ -1,9 +1,9 @@ - + - + @@ -661,7 +661,7 @@ - + diff --git a/Example/Tests/DSCoinJoinSessionTest.m b/Example/Tests/DSCoinJoinSessionTest.m index b1b26878b..2d1fa6c0a 100644 --- a/Example/Tests/DSCoinJoinSessionTest.m +++ b/Example/Tests/DSCoinJoinSessionTest.m @@ -31,7 +31,7 @@ - (void)sessionTest { options->coinjoin_rounds = 1; options->coinjoin_sessions = 1; options->coinjoin_amount = DUFFS / 4; // 0.25 DASH - options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; + options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; // TODO: check options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; options->coinjoin_multi_session = NO; From e3721544e3a65950bcb88321b15ff882bf4f7ebb Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 20 Aug 2024 13:59:17 +0700 Subject: [PATCH 037/133] fix: empty inputs for anyonecanpay --- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 44 ++++++----- .../Models/Transactions/Base/DSTransaction.m | 78 ++++++++++++------- 2 files changed, 72 insertions(+), 50 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index c8cae9d46..abb549e80 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -115,9 +115,9 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { } - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { - DSLog(@"[OBJ-C] CoinJoin connect: disconnect mn: %@", [self hostFor:ip]); + DSLog(@"[OBJ-C] CoinJoin peers: disconnect mn: %@", [self hostFor:ip]); return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { - DSLog(@"[OBJ-C] CoinJoin connect: masternode[closing] %@", [self hostFor:ip]); + DSLog(@"[OBJ-C] CoinJoin peers: masternode[closing] %@", [self hostFor:ip]); @synchronized (self.pendingClosingMasternodes) { [self.pendingClosingMasternodes addObject:peer]; @@ -198,7 +198,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized (self.inactives) { BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; - DSLog(@"[OBJ-C] CoinJoin connect: havPeersToTry: %s", havPeersToTry ? "YES" : "NO"); + DSLog(@"[OBJ-C] CoinJoin peers: havPeersToTry: %s", havPeersToTry ? "YES" : "NO"); doDiscovery = !havPeersToTry; NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; DSPeer *peerToTry = nil; @@ -249,13 +249,13 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; } - DSLog(@"[OBJ-C] CoinJoin connect: retry time for: %@ is %@", peerToTry.location, retryTime); + DSLog(@"[OBJ-C] CoinJoin peers: retry time for: %@ is %@", peerToTry.location, retryTime); if (numPeers > 0) { retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; } - DSLog(@"[OBJ-C] CoinJoin connect: final retry time for: %@ is %@", peerToTry.location, retryTime); + DSLog(@"[OBJ-C] CoinJoin peers: final retry time for: %@ is %@", peerToTry.location, retryTime); NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; if (delay > 0) { @@ -334,7 +334,7 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self.pendingSessions) { - DSLog(@"[OBJ-C] CoinJoin connect: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); + DSLog(@"[OBJ-C] CoinJoin peers: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [self.pendingSessions addObject:sessionIdValue]; @@ -346,10 +346,11 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; if (mixingMasternodeInfo) { + DSLog(@"[OBJ-C] CoinJoin peers mnInfo uniqueID: %@", mixingMasternodeInfo.uniqueID); ipAddress = mixingMasternodeInfo.address; } - DSLog(@"[OBJ-C] CoinJoin connect: adding masternode for mixing. location: %@", [self hostFor:ipAddress]); + DSLog(@"[OBJ-C] CoinJoin peers: adding masternode for mixing. location: %@", [self hostFor:ipAddress]); // ~~~~~~~~~~ @@ -361,10 +362,10 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session } - (void)updateMaxConnections { - DSLog(@"[OBJ-C] CoinJoin connect: updateMaxConnections, pendingSessions.count: %lu", self.pendingSessions.count); + DSLog(@"[OBJ-C] CoinJoin peers: updateMaxConnections, pendingSessions.count: %lu", self.pendingSessions.count); _maxConnections = self.pendingSessions.count; NSUInteger connections = MIN(_maxConnections, DEFAULT_COINJOIN_SESSIONS); - DSLog(@"[OBJ-C] CoinJoin connect: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); + DSLog(@"[OBJ-C] CoinJoin peers: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); [self updateMaxConnections:connections]; } @@ -385,16 +386,17 @@ - (void)updateMaxConnections:(NSUInteger)connections { NSUInteger connectedCount = connectedPeers.count; NSUInteger numPeers = pendingCount + connectedCount; adjustment = self.maxConnections - numPeers; - DSLog(@"[OBJ-C] CoinJoin connect: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); + DSLog(@"[OBJ-C] CoinJoin peers: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); } if (adjustment > 0) { - DSLog(@"[OBJ-C] CoinJoin connect: triggerConnections for adjustment"); + DSLog(@"[OBJ-C] CoinJoin peers: triggerConnections for adjustment"); [self triggerConnections]; } if (adjustment < 0) { for (DSPeer *peer in connectedPeers) { + DSLog(@"[OBJ-C] CoinJoin peers: adjustment < 0, disconnecting peer %@", peer.location); [peer disconnect]; adjustment++; @@ -425,21 +427,21 @@ - (void)checkMasternodesWithoutSessions { } } else { // TODO(DashJ): we may not need this anymore - DSLog(@"[OBJ-C] CoinJoin connect: session is not connected to a masternode: %@", uint256_hex(sessionId)); + DSLog(@"[OBJ-C] CoinJoin peers: session is not connected to a masternode: %@", uint256_hex(sessionId)); } } if (!found) { - DSLog(@"[OBJ-C] CoinJoin connect: masternode is not connected to a session: %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin peers: masternode is not connected to a session: %@", peer.location); [masternodesToDrop addObject:peer]; } } - DSLog(@"[OBJ-C] CoinJoin connect: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); + DSLog(@"[OBJ-C] CoinJoin peers: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; - DSLog(@"[OBJ-C] CoinJoin connect: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + DSLog(@"[OBJ-C] CoinJoin peers: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); [peer disconnect]; } } @@ -547,7 +549,7 @@ - (void)peerConnected:(nonnull DSPeer *)peer { } - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { - DSLog(@"[OBJ-C] CoinJoin connect: %@ disconnectedWithError %@", peer.location, error); + DSLog(@"[OBJ-C] CoinJoin peers: %@ disconnectedWithError %@", peer.location, error); @synchronized (self) { [self.mutablePendingPeers removeObject:peer]; @@ -562,7 +564,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; if (numPeers < self.maxConnections) { - DSLog(@"[OBJ-C] CoinJoin connect: triggerConnections to get to maxConnections"); + DSLog(@"[OBJ-C] CoinJoin peers: triggerConnections to get to maxConnections"); [self triggerConnections]; } } @@ -576,7 +578,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err } } - DSLog(@"[OBJ-C] CoinJoin connect: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"[OBJ-C] CoinJoin peers: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); if (masternode) { NSString *address = peer.location; @@ -628,14 +630,14 @@ - (void)addInactive:(DSPeer *)peer { - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { for (DSPeer *peer in self.connectedPeers) { if (uint128_eq(peer.address, ipAddress) && peer.port == port) { - DSLog(@"[OBJ-C] CoinJoin connect: peerForLocation found in connectedPeers"); + DSLog(@"[OBJ-C] CoinJoin peers: peerForLocation found in connectedPeers"); return peer; } } for (DSPeer *peer in self.pendingPeers) { if (uint128_eq(peer.address, ipAddress) && peer.port == port) { - DSLog(@"[OBJ-C] CoinJoin connect: peerForLocation found in pendingPeers"); + DSLog(@"[OBJ-C] CoinJoin peers: peerForLocation found in pendingPeers"); return peer; } } @@ -665,7 +667,7 @@ - (void)sortInactives { - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.lastSeenBlock = self.chain.lastSyncBlock.height; - DSLog(@"[OBJ-C] CoinJoin connect: new block found, restarting masternode connections job"); + DSLog(@"[OBJ-C] CoinJoin peers: new block found, restarting masternode connections job"); [self triggerConnections]; } } diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index f2d9ba580..c39938e03 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -84,7 +84,7 @@ - (instancetype)init { - (instancetype)initOnChain:(DSChain *)chain { if (!(self = [super init])) return nil; - + _version = TX_VERSION; self.mInputs = [NSMutableArray array]; self.mOutputs = [NSMutableArray array]; @@ -100,12 +100,12 @@ - (instancetype)initOnChain:(DSChain *)chain { - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { if (!(self = [self initOnChain:chain])) return nil; - + NSString *address = nil; NSNumber *l = 0; uint32_t off = 0; uint64_t count = 0; - + @autoreleasepool { self.chain = chain; _version = [message UInt16AtOffset:off]; // tx version @@ -117,7 +117,7 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { return nil; // at least one input is required } off += l.unsignedIntegerValue; - + for (NSUInteger i = 0; i < count; i++) { // inputs UInt256 hash = [message UInt256AtOffset:off]; // input hash off += sizeof(UInt256); @@ -131,10 +131,10 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { DSTransactionInput *transactionInput = [DSTransactionInput transactionInputWithHash:hash index:index inScript:inScript signature:signature sequence:sequence]; [self.mInputs addObject:transactionInput]; } - + count = (NSUInteger)[message varIntAtOffset:off length:&l]; // output count off += l.unsignedIntegerValue; - + for (NSUInteger i = 0; i < count; i++) { // outputs uint64_t amount = [message UInt64AtOffset:off]; // output amount off += sizeof(uint64_t); @@ -143,7 +143,7 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { DSTransactionOutput *transactionOutput = [DSTransactionOutput transactionOutputWithAmount:amount outScript:outScript onChain:self.chain]; [self.mOutputs addObject:transactionOutput]; } - + _lockTime = [message UInt32AtOffset:off]; // tx locktime off += 4; _payloadOffset = off; @@ -151,9 +151,9 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { _txHash = self.data.SHA256_2; } } - + if ([self type] != DSTransactionType_Classic) return self; //only classic transactions are shapeshifted - + NSString *outboundShapeshiftAddress = [self shapeshiftOutboundAddress]; if (!outboundShapeshiftAddress) return self; self.associatedShapeshift = [DSShapeshiftEntity shapeshiftHavingWithdrawalAddress:outboundShapeshiftAddress inContext:[NSManagedObjectContext chainContext]]; @@ -167,9 +167,9 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { self.associatedShapeshift.shapeshiftStatus = @(eShapeshiftAddressStatus_NoDeposits); } } - + if (self.associatedShapeshift || ![self.outputs count]) return self; - + NSString *mainOutputAddress = nil; NSMutableArray *allAddresses = [NSMutableArray array]; for (DSAddressEntity *e in [DSAddressEntity allObjectsInContext:chain.chainManagedObjectContext]) { @@ -185,7 +185,7 @@ - (instancetype)initWithMessage:(NSData *)message onChain:(DSChain *)chain { if (mainOutputAddress) { self.associatedShapeshift = [DSShapeshiftEntity registerShapeshiftWithInputAddress:mainOutputAddress andWithdrawalAddress:outboundShapeshiftAddress withStatus:eShapeshiftAddressStatus_NoDeposits inContext:[NSManagedObjectContext chainContext]]; } - + return self; } @@ -197,9 +197,9 @@ - (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)in if (hashes.count != indexes.count) return nil; if (scripts.count > 0 && hashes.count != scripts.count) return nil; if (addresses.count != amounts.count) return nil; - + if (!(self = [super init])) return nil; - + self.persistenceStatus = DSTransactionPersistenceStatus_NotSaved; self.chain = chain; _version = chain.transactionVersion; @@ -213,7 +213,7 @@ - (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)in NSData *inputScript = (scripts.count > 0) ? [scripts objectAtIndex:i] : nil; [self.mInputs addObject:[DSTransactionInput transactionInputWithHash:inputHash index:index inScript:inputScript signature:nil sequence:inputSequence]]; } - + self.mOutputs = [NSMutableArray array]; for (int i = 0; i < amounts.count; i++) { uint64_t amount = [[amounts objectAtIndex:i] unsignedLongValue]; @@ -226,7 +226,7 @@ - (instancetype)initWithInputHashes:(NSArray *)hashes inputIndexes:(NSArray *)in } [self.mOutputs addObject:[DSTransactionOutput transactionOutputWithAmount:amount outScript:outScript onChain:self.chain]]; } - + _lockTime = TX_LOCKTIME; self.blockHeight = TX_UNCONFIRMED; return self; @@ -313,9 +313,9 @@ - (NSString *)description { - (NSString *)longDescription { NSString *txid = [NSString hexWithData:[NSData dataWithBytes:self.txHash.u8 length:sizeof(UInt256)].reverse]; return [NSString stringWithFormat:@"%@(id=%@, inputs=%@, outputs=%@)", - [[self class] description], txid, - self.inputs, - self.outputs]; + [[self class] description], txid, + self.inputs, + self.outputs]; } // retuns the amount sent from the wallet by the trasaction (total wallet outputs consumed, change and fee included) @@ -342,7 +342,7 @@ - (size_t)size { uint32_t inputCount = (uint32_t)self.mInputs.count; uint32_t outputCount = (uint32_t)self.mOutputs.count; return 8 + [NSMutableData sizeOfVarInt:inputCount] + [NSMutableData sizeOfVarInt:outputCount] + - TX_INPUT_SIZE * inputCount + TX_OUTPUT_SIZE * outputCount; + TX_INPUT_SIZE * inputCount + TX_OUTPUT_SIZE * outputCount; } } @@ -356,10 +356,13 @@ - (uint64_t)standardInstantFee { // checks if all signatures exist, but does not verify them - (BOOL)isSigned { + return [self isSigned:NO]; +} +- (BOOL)isSigned:(BOOL)anyoneCanPay { @synchronized (self) { BOOL isSigned = TRUE; for (DSTransactionInput *transactionInput in self.mInputs) { - BOOL inputIsSigned = transactionInput.signature != nil; + BOOL inputIsSigned = anyoneCanPay || transactionInput.signature != nil; isSigned &= inputIsSigned; if (!inputIsSigned) { break; @@ -410,7 +413,11 @@ - (NSUInteger)hash { // MARK: - Wire Serialization - (NSData *)toData { - return [self toDataWithSubscriptIndex:NSNotFound]; + return [self toData:NO]; +} + +- (NSData *)toData:(BOOL)anyoneCanPay { + return [self toDataWithSubscriptIndex:NSNotFound anyoneCanPay:anyoneCanPay]; } // Returns the binary transaction data that needs to be hashed and signed with the private key for the tx input at @@ -425,12 +432,25 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BO NSArray *outputs = self.outputs; NSUInteger inputsCount = inputs.count; NSUInteger outputsCount = outputs.count; + + if (anyoneCanPay) { + if (subscriptIndex < inputsCount) { + inputs = @[inputs[subscriptIndex]]; + inputsCount = 1; + } else { + // Handle error: subscriptIndex out of bounds + NSAssert(NO, @"CoinJoin Error: subscriptIndex %lu is out of bounds for inputs array", (unsigned long)subscriptIndex); + } + } + BOOL forSigHash = ([self isMemberOfClass:[DSTransaction class]] || [self isMemberOfClass:[DSCreditFundingTransaction 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]; [d appendUInt16:self.version]; -// [d appendUInt16:self.type]; + if (!anyoneCanPay) { + [d appendUInt16:self.type]; + } [d appendVarInt:inputsCount]; for (NSUInteger i = 0; i < inputsCount; i++) { @@ -440,7 +460,7 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BO if (subscriptIndex == NSNotFound && input.signature != nil) { [d appendCountedData:input.signature]; - } else if (subscriptIndex == i && input.inScript != nil) { + } else if (anyoneCanPay || (subscriptIndex == i && input.inScript != nil)) { // TODO: to fully match the reference implementation, OP_CODESEPARATOR related checksig logic should go here [d appendCountedData:input.inScript]; } else { @@ -611,9 +631,9 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { NSString *addr = [DSKeyManager addressWithScriptPubKey:transactionInput.inScript forChain:self.chain]; NSUInteger keyIdx = (addr) ? [addresses indexOfObject:addr] : NSNotFound; if (keyIdx == NSNotFound) { - if (anyoneCanPay && !transactionInput.signature) { - transactionInput.signature = [NSData data]; - } +// if (anyoneCanPay && !transactionInput.signature) { +// transactionInput.signature = [NSData data]; +// } continue; } @@ -638,8 +658,8 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { transactionInput.signature = sig; } - if (!self.isSigned) return NO; - _txHash = self.data.SHA256_2; + if (![self isSigned:anyoneCanPay]) return NO; + _txHash = [self toData:anyoneCanPay].SHA256_2; return YES; } } From a3adbc3c732826e88fca2524cd2d408e722c903c Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 22 Aug 2024 22:50:11 +0700 Subject: [PATCH 038/133] fix: signed inputs --- .../Models/CoinJoin/DSCoinJoinWrapper.m | 2 +- .../Models/CoinJoin/DSTransaction+CoinJoin.m | 5 +- .../CoinJoin/DSTransactionOutput+CoinJoin.m | 13 ++-- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 72 +++++++------------ .../Models/Transactions/Base/DSTransaction.m | 28 +++----- 5 files changed, 50 insertions(+), 70 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 65484839a..119d5e71d 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -114,7 +114,7 @@ - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSStri if (array->ptr) { free((void *)array->ptr); } - + free(array); } } diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m index 946948d3a..fdad29ec1 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -69,7 +69,10 @@ - (DSTransaction *)initWithTransaction:(Transaction *)transaction onChain:(DSCha [amounts addObject:amount]; } - return [[DSTransaction alloc] initWithInputHashes:hashes inputIndexes:indexes inputScripts:scripts inputSequences:inputSequences outputAddresses:addresses outputAmounts:amounts onChain:chain]; + DSTransaction *tx = [[DSTransaction alloc] initWithInputHashes:hashes inputIndexes:indexes inputScripts:scripts inputSequences:inputSequences outputAddresses:addresses outputAmounts:amounts onChain:chain]; + tx.version = transaction->version; + + return tx; } - (Transaction *)ffi_malloc:(ChainType)chainType { diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m index 2caadec3f..2f74c0377 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m @@ -32,9 +32,14 @@ - (TransactionOutput *)ffi_malloc:(ChainType)type { transactionOutput->script = data_malloc(scriptData); char *c_string = address_with_script_pubkey(self.outScript.bytes, self.outScript.length, type); - size_t addressLength = strlen(c_string); - transactionOutput->address_length = (uintptr_t)addressLength; - transactionOutput->address = (uint8_t *)c_string; + + if (c_string) { + size_t addressLength = strlen(c_string); + transactionOutput->address_length = (uintptr_t)addressLength; + transactionOutput->address = (uint8_t *)c_string; + } else { + transactionOutput->address_length = 0; + } return transactionOutput; } @@ -46,7 +51,7 @@ + (void)ffi_free:(TransactionOutput *)output { free(output->script); } - if (output->address) { + if (output->address && output->address_length > 0) { // TODO: should we use processor_destroy_string(c_string) here? free(output->address); } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index abb549e80..e833d645d 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -40,6 +40,7 @@ @interface DSMasternodeGroup () @property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; // TODO: sync all access points @property (nonatomic, strong) NSMutableSet *pendingSessions; @property (nonatomic, strong) NSMutableDictionary *masternodeMap; +@property (nonatomic, strong) NSMutableDictionary *sessionMap; @property (nonatomic, strong) NSMutableDictionary *addressMap; @property (atomic, readonly) NSUInteger maxConnections; @property (nonatomic, strong) NSMutableArray *pendingClosingMasternodes; @@ -66,6 +67,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _pendingSessions = [NSMutableSet set]; _pendingClosingMasternodes = [NSMutableArray array]; _masternodeMap = [NSMutableDictionary dictionary]; + _sessionMap = [NSMutableDictionary dictionary]; _addressMap = [NSMutableDictionary dictionary]; _mutableConnectedPeers = [NSMutableSet set]; _mutablePendingPeers = [NSMutableSet set]; @@ -115,7 +117,6 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { } - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { - DSLog(@"[OBJ-C] CoinJoin peers: disconnect mn: %@", [self hostFor:ip]); return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { DSLog(@"[OBJ-C] CoinJoin peers: masternode[closing] %@", [self hostFor:ip]); @@ -198,7 +199,6 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized (self.inactives) { BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; - DSLog(@"[OBJ-C] CoinJoin peers: havPeersToTry: %s", havPeersToTry ? "YES" : "NO"); doDiscovery = !havPeersToTry; NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; DSPeer *peerToTry = nil; @@ -249,13 +249,10 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; } - DSLog(@"[OBJ-C] CoinJoin peers: retry time for: %@ is %@", peerToTry.location, retryTime); - if (numPeers > 0) { retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; } - DSLog(@"[OBJ-C] CoinJoin peers: final retry time for: %@ is %@", peerToTry.location, retryTime); NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; if (delay > 0) { @@ -282,17 +279,14 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized(self.addressMap) { NSArray *pendingSessionsCopy = [self.pendingSessions copy]; - DSLog(@"[OBJ-C] CoinJoin getPeers, pendingSessions len: %lu", (unsigned long)pendingSessionsCopy.count); for (NSValue *sessionValue in pendingSessionsCopy) { UInt256 sessionId; [sessionValue getValue:&sessionId]; - DSLog(@"[OBJ-C] CoinJoin getPeers, sessionId: %@", uint256_hex(sessionId)); DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; - + if (mixingMasternodeInfo) { UInt128 ipAddress = mixingMasternodeInfo.address; - DSLog(@"[OBJ-C] CoinJoin getPeers, ipAddress: %@", [self hostFor:ipAddress]); uint16_t port = mixingMasternodeInfo.port; DSPeer *peer = [self peerForLocation:ipAddress port:port]; @@ -316,17 +310,14 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { - for (NSValue* key in self.masternodeMap) { - NSValue *object = [self.masternodeMap objectForKey:key]; - UInt256 currentId = UINT256_ZERO; - [object getValue:¤tId]; + NSValue *sessionIdKey = [NSValue value:&sessionId withObjCType:@encode(UInt256)]; + NSValue *proTxHashValue = [self.sessionMap objectForKey:sessionIdKey]; + + if (proTxHashValue) { + UInt256 proTxHash = UINT256_ZERO; + [proTxHashValue getValue:&proTxHash]; - if (uint256_eq(sessionId, currentId)) { - UInt256 proTxHash = UINT256_ZERO; - [key getValue:&proTxHash]; - - return [self.coinJoinManager masternodeEntryByHash:proTxHash]; - } + return [self.coinJoinManager masternodeEntryByHash:proTxHash]; } return nil; @@ -340,19 +331,7 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; [self.masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; - - // ~~~~~~~~~ - UInt128 ipAddress = UINT128_ZERO; - DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; - - if (mixingMasternodeInfo) { - DSLog(@"[OBJ-C] CoinJoin peers mnInfo uniqueID: %@", mixingMasternodeInfo.uniqueID); - ipAddress = mixingMasternodeInfo.address; - } - - DSLog(@"[OBJ-C] CoinJoin peers: adding masternode for mixing. location: %@", [self hostFor:ipAddress]); - - // ~~~~~~~~~~ + [self.sessionMap setObject:proTxHashKey forKey:sessionIdValue]; [self updateMaxConnections]; [self checkMasternodesWithoutSessions]; @@ -471,13 +450,13 @@ - (BOOL)connectTo:(DSPeer *)peer { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; UInt256 sessionId = UINT256_ZERO; - @synchronized (_masternodeMap) { + @synchronized (self.masternodeMap) { UInt256 proTxHash = mn.providerRegistrationTransactionHash; NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; - NSValue *keyObject = [_masternodeMap objectForKey:proTxHashKey]; + NSValue *sessionObject = [self.masternodeMap objectForKey:proTxHashKey]; - if (keyObject) { - [keyObject getValue:&sessionId]; + if (sessionObject) { + [sessionObject getValue:&sessionId]; } } @@ -502,7 +481,6 @@ - (BOOL)connectTo:(DSPeer *)peer { [self.mutablePendingPeers addObject:peer]; } - DSLog(@"[OBJ-C] CoinJoin: calling peer.connect to %@", peer.location); [peer connect]; return YES; @@ -590,10 +568,16 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err } [self.pendingClosingMasternodes removeObject:masternode]; - UInt256 sessionId = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; - NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; - [self.pendingSessions removeObject:sessionIdValue]; - [self.masternodeMap removeObjectForKey:sessionIdValue]; + UInt256 proTxHash = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; + NSValue *proTxHashKey = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; + NSValue *sessionIdObject = [self.masternodeMap objectForKey:proTxHashKey]; + + if (sessionIdObject) { + [self.pendingSessions removeObject:sessionIdObject]; + [self.sessionMap removeObjectForKey:sessionIdObject]; + } + + [self.masternodeMap removeObjectForKey:proTxHashKey]; [self.addressMap removeObjectForKey:masternode.location]; } @@ -606,7 +590,7 @@ - (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { } - (void)addInactive:(DSPeer *)peer { - DSLog(@"[OBJ-C] CoinJoin peers: addInactive: %@, currentCount: %lu, isConnected: %s, isConnecting: %s", peer.location, (unsigned long)self.inactives.count, peer.status == DSPeerStatus_Connected ? "YES" : "NO", peer.status == DSPeerStatus_Connecting ? "YES" : "NO"); + DSLog(@"[OBJ-C] CoinJoin peers: addInactive: %@, currentCount: %lu", peer.location, (unsigned long)self.inactives.count); @synchronized (self.inactives) { // Deduplicate, handle differently than PeerGroup @@ -622,7 +606,7 @@ - (void)addInactive:(DSPeer *)peer { DSBackoff *backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:BACKOFF_MULTIPLIER]; [self.backoffMap setObject:backoff forKey:peer.location]; - [self.inactives addObject:peer]; + [self.inactives insertObject:peer atIndex:0]; [self sortInactives]; } } @@ -630,14 +614,12 @@ - (void)addInactive:(DSPeer *)peer { - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { for (DSPeer *peer in self.connectedPeers) { if (uint128_eq(peer.address, ipAddress) && peer.port == port) { - DSLog(@"[OBJ-C] CoinJoin peers: peerForLocation found in connectedPeers"); return peer; } } for (DSPeer *peer in self.pendingPeers) { if (uint128_eq(peer.address, ipAddress) && peer.port == port) { - DSLog(@"[OBJ-C] CoinJoin peers: peerForLocation found in pendingPeers"); return peer; } } diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index c39938e03..814b396b5 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -356,13 +356,10 @@ - (uint64_t)standardInstantFee { // checks if all signatures exist, but does not verify them - (BOOL)isSigned { - return [self isSigned:NO]; -} -- (BOOL)isSigned:(BOOL)anyoneCanPay { @synchronized (self) { BOOL isSigned = TRUE; for (DSTransactionInput *transactionInput in self.mInputs) { - BOOL inputIsSigned = anyoneCanPay || transactionInput.signature != nil; + BOOL inputIsSigned = transactionInput.signature != nil; isSigned &= inputIsSigned; if (!inputIsSigned) { break; @@ -433,14 +430,9 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BO NSUInteger inputsCount = inputs.count; NSUInteger outputsCount = outputs.count; - if (anyoneCanPay) { - if (subscriptIndex < inputsCount) { - inputs = @[inputs[subscriptIndex]]; - inputsCount = 1; - } else { - // Handle error: subscriptIndex out of bounds - NSAssert(NO, @"CoinJoin Error: subscriptIndex %lu is out of bounds for inputs array", (unsigned long)subscriptIndex); - } + if (anyoneCanPay && subscriptIndex < inputsCount) { + inputs = @[inputs[subscriptIndex]]; + inputsCount = 1; } BOOL forSigHash = ([self isMemberOfClass:[DSTransaction class]] || [self isMemberOfClass:[DSCreditFundingTransaction class]]) && subscriptIndex != NSNotFound; @@ -448,9 +440,7 @@ - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BO NSMutableData *d = [NSMutableData dataWithCapacity:dataSize]; [d appendUInt16:self.version]; - if (!anyoneCanPay) { - [d appendUInt16:self.type]; - } + [d appendUInt16:self.type]; [d appendVarInt:inputsCount]; for (NSUInteger i = 0; i < inputsCount; i++) { @@ -631,9 +621,9 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { NSString *addr = [DSKeyManager addressWithScriptPubKey:transactionInput.inScript forChain:self.chain]; NSUInteger keyIdx = (addr) ? [addresses indexOfObject:addr] : NSNotFound; if (keyIdx == NSNotFound) { -// if (anyoneCanPay && !transactionInput.signature) { -// transactionInput.signature = [NSData data]; -// } + if (anyoneCanPay && !transactionInput.signature) { + transactionInput.signature = [NSData data]; + } continue; } @@ -658,7 +648,7 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { transactionInput.signature = sig; } - if (![self isSigned:anyoneCanPay]) return NO; + if (!self.isSigned) return NO; _txHash = [self toData:anyoneCanPay].SHA256_2; return YES; } From 78044d7356bfd8a33a68f92385bac473234fe5f5 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Fri, 23 Aug 2024 18:01:06 +0700 Subject: [PATCH 039/133] feat: remove getIdentityIdsByKeyHashes and edit getIdentitiesByKeyHashes --- .../Networking/DSDAPIPlatformNetworkService.m | 87 ++++++++++++------- .../DSDAPIPlatformNetworkServiceProtocol.h | 26 ++---- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m index eb061c47b..da679a3e6 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m @@ -450,27 +450,6 @@ - (void)loadBloomFilter:(NSString *)filter #pragma mark Layer 2 -- (id)fetchIdentityIdsByKeyHashes:(NSArray *)keyHashesArray - completionQueue:(dispatch_queue_t)completionQueue - success:(void (^)(NSArray *identityIds))success - failure:(void (^)(NSError *error))failure { - NSParameterAssert(keyHashesArray); - NSParameterAssert(completionQueue); - DSPlatformRequestLog(@"fetchIdentityIdsByKeyHashes %@", keyHashesArray); - GetIdentityIdsByPublicKeyHashesRequest *getIdentityIdsByPublicKeyHashesRequest = [[GetIdentityIdsByPublicKeyHashesRequest alloc] init]; - getIdentityIdsByPublicKeyHashesRequest.publicKeyHashesArray = [keyHashesArray mutableCopy]; - getIdentityIdsByPublicKeyHashesRequest.prove = DSPROVE_PLATFORM; - DSDAPIGRPCResponseHandler *responseHandler = [[DSDAPIGRPCResponseHandler alloc] initForGetIdentityIDsByPublicKeyHashesRequest:keyHashesArray 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 = success; - responseHandler.errorHandler = failure; - GRPCUnaryProtoCall *call = [self.gRPCClient getIdentityIdsByPublicKeyHashesWithMessage:getIdentityIdsByPublicKeyHashesRequest responseHandler:responseHandler callOptions:nil]; - [call start]; - return (id)call; -} - - (id)fetchIdentitiesByKeyHashes:(NSArray *)keyHashesArray completionQueue:(dispatch_queue_t)completionQueue success:(void (^)(NSArray *identityDictionaries))success @@ -478,18 +457,60 @@ - (void)loadBloomFilter:(NSString *)filter NSParameterAssert(keyHashesArray); NSParameterAssert(completionQueue); DSPlatformRequestLog(@"fetchIdentitiesByKeyHashes %@", keyHashesArray); - GetIdentitiesByPublicKeyHashesRequest *getIdentitiesByPublicKeyHashesRequest = [[GetIdentitiesByPublicKeyHashesRequest alloc] init]; - getIdentitiesByPublicKeyHashesRequest.publicKeyHashesArray = [keyHashesArray mutableCopy]; - getIdentitiesByPublicKeyHashesRequest.prove = DSPROVE_PLATFORM; - DSDAPIGRPCResponseHandler *responseHandler = [[DSDAPIGRPCResponseHandler alloc] initForGetIdentitiesByPublicKeyHashesRequest:keyHashesArray 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 = success; - responseHandler.errorHandler = failure; - GRPCUnaryProtoCall *call = [self.gRPCClient getIdentitiesByPublicKeyHashesWithMessage:getIdentitiesByPublicKeyHashesRequest responseHandler:responseHandler callOptions:nil]; - [call start]; - return (id)call; + + 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]; + } + + return nil; } - (id)fetchContractForId:(NSData *)contractId diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h index ae5dc9118..ad67d2e08 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h @@ -487,31 +487,21 @@ Get a list of users after matching search criteria failure:(void (^)(NSError *error))failure; /** -Get a list of identities knowing only keys they possess +Get a list of identities corresponding to the provided key hashes. -@param keyHashesArray An array of hashes of keys -@param completionQueue The queue in which to return the result on -@param success A block object to be executed when the request operation finishes successfully -@param failure A block object to be executed when the request operation finishes unsuccessfully +This method makes individual requests for each key hash in the array and returns a list of identity dictionaries upon successful completion. If any request fails, the failure block is called. + +@param keyHashesArray An array of hashes of keys for which to fetch identities. +@param completionQueue The queue on which to execute the success or failure block. +@param success A block object to be executed when all requests finish successfully, providing an array of identity dictionaries. +@param failure A block object to be executed if any request fails, providing the error encountered. +@return Returns an object conforming to `DSDAPINetworkServiceRequest`, or `nil` if no request is made. */ - (id)fetchIdentitiesByKeyHashes:(NSArray *)keyHashesArray completionQueue:(dispatch_queue_t)completionQueue success:(void (^)(NSArray *identityDictionaries))success failure:(void (^)(NSError *error))failure; -/** -Get a list of identity Ids knowing only keys they possess - -@param keyHashesArray An array of hashes of keys -@param completionQueue The queue in which to return the result on -@param success A block object to be executed when the request operation finishes successfully -@param failure A block object to be executed when the request operation finishes unsuccessfully -*/ -- (id)fetchIdentityIdsByKeyHashes:(NSArray *)keyHashesArray - completionQueue:(dispatch_queue_t)completionQueue - success:(void (^)(NSArray *identityIds))success - failure:(void (^)(NSError *error))failure; - @end NS_ASSUME_NONNULL_END From b2930481dda93802842c71fee419e88312098779 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Sat, 24 Aug 2024 19:33:44 +0700 Subject: [PATCH 040/133] fix: address dpns contract changes --- .../Models/DAPI/DSPlatformDocumentsRequest.m | 8 +- .../Models/Identity/DSBlockchainIdentity.m | 2 +- DashSync/shared/dpns-contract.json | 97 ++++++++++++------- 3 files changed, 67 insertions(+), 40 deletions(-) diff --git a/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m b/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m index 1fde02804..5019d61fc 100644 --- a/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m +++ b/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m @@ -57,7 +57,7 @@ + (instancetype)dpnsRequestForUsername:(NSString *)username inDomain:(NSString * + (instancetype)dpnsRequestForUserId:(NSData *)userId { DSPlatformDocumentsRequest *platformDocumentsRequest = [[DSPlatformDocumentsRequest alloc] init]; - platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"records.dashUniqueIdentityId == %@", userId]; + platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"records.identity == %@", userId]; // why not path predicate and then predicate? platformDocumentsRequest.startAt = nil; platformDocumentsRequest.limit = 100; platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverIndex; @@ -115,7 +115,7 @@ + (instancetype)dashpayRequestForContactRequestsForSendingUserId:(NSData *)userI platformDocumentsRequest.startAtIncluded = false; platformDocumentsRequest.limit = 100; platformDocumentsRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"$createdAt" ascending:YES]]; - platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; + platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; // why not over index? platformDocumentsRequest.type = DSPlatformDocumentType_Document; platformDocumentsRequest.tableName = @"contactRequest"; platformDocumentsRequest.prove = DSPROVE_PLATFORM_SINDEXES; @@ -131,7 +131,7 @@ + (instancetype)dashpayRequestForContactRequestsForRecipientUserId:(NSData *)use platformDocumentsRequest.startAtIncluded = false; platformDocumentsRequest.limit = 100; platformDocumentsRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"$createdAt" ascending:YES]]; - platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; + platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; // why not over index? platformDocumentsRequest.type = DSPlatformDocumentType_Document; platformDocumentsRequest.tableName = @"contactRequest"; platformDocumentsRequest.prove = DSPROVE_PLATFORM_SINDEXES; @@ -140,7 +140,7 @@ + (instancetype)dashpayRequestForContactRequestsForRecipientUserId:(NSData *)use + (instancetype)dashpayRequestForContactRequestForSendingUserId:(NSData *)userId toRecipientUserId:(NSData *)toUserId { DSPlatformDocumentsRequest *platformDocumentsRequest = [[DSPlatformDocumentsRequest alloc] init]; - platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"%K == %@ && toUserId == %@", @"$ownerId", userId, toUserId]; + platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"%K == %@ && toUserId == %@", @"$ownerId", userId, toUserId]; // why not path predicate and predicate? platformDocumentsRequest.startAt = nil; platformDocumentsRequest.limit = 100; platformDocumentsRequest.type = DSPlatformDocumentType_Document; diff --git a/DashSync/shared/Models/Identity/DSBlockchainIdentity.m b/DashSync/shared/Models/Identity/DSBlockchainIdentity.m index f62ffd288..bfc7b862f 100644 --- a/DashSync/shared/Models/Identity/DSBlockchainIdentity.m +++ b/DashSync/shared/Models/Identity/DSBlockchainIdentity.m @@ -2197,7 +2197,7 @@ - (NSString *)dashpayDomainName { @"normalizedLabel": [username lowercaseString], @"normalizedParentDomainName": domain, @"preorderSalt": [self.usernameSalts objectForKey:usernameFullPath], - @"records": @{@"dashUniqueIdentityId": uint256_data(self.uniqueID)}, + @"records": @{@"identity": uint256_data(self.uniqueID)}, @"subdomainRules": @{@"allowSubdomains": @NO} }; DPDocument *document = [self.dpnsDocumentFactory documentOnTable:@"domain" withDataDictionary:dataDictionary usingEntropy:entropyData error:error]; diff --git a/DashSync/shared/dpns-contract.json b/DashSync/shared/dpns-contract.json index 81692dd88..b620bb79c 100644 --- a/DashSync/shared/dpns-contract.json +++ b/DashSync/shared/dpns-contract.json @@ -1,8 +1,14 @@ { "documents": { "domain": { + "documentsMutable": false, + "canBeDeleted": true, + "transferable": 1, + "tradeMode": 1, + "type": "object", "indices": [ { + "name": "parentNameAndLabel", "properties": [ { "normalizedParentDomainName": "asc" @@ -11,20 +17,24 @@ "normalizedLabel": "asc" } ], - "unique": true + "unique": true, + "contested": { + "fieldMatches": [ + { + "field": "normalizedLabel", + "regexPattern": "^[a-zA-Z01-]{3,19}$" + } + ], + "resolution": 0, + "description": "If the normalized label part of this index is less than 20 characters (all alphabet a-z, A-Z, 0, 1, and -) then a masternode vote contest takes place to give out the name" + } }, { + "name": "identityId", + "nullSearchable": false, "properties": [ { - "records.dashUniqueIdentityId": "asc" - } - ], - "unique": true - }, - { - "properties": [ - { - "records.dashAliasIdentityId": "asc" + "records.identity": "asc" } ] } @@ -32,58 +42,60 @@ "properties": { "label": { "type": "string", - "pattern": "^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9])$", + "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$", "minLength": 3, "maxLength": 63, + "position": 0, "description": "Domain label. e.g. 'Bob'." }, "normalizedLabel": { "type": "string", - "pattern": "^((?!-)[a-z0-9-]{0,62}[a-z0-9])$", + "pattern": "^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-]{0,61}[a-hj-km-np-z0-9]$", "maxLength": 63, - "description": "Domain label in lowercase for case-insensitive uniqueness validation. e.g. 'bob'", - "$comment": "Must be equal to the label in lowercase. This property will be deprecated due to case insensitive indices" + "position": 1, + "description": "Domain label converted to lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'b0b'", + "$comment": "Must be equal to the label in lowercase. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\"." + }, + "parentDomainName": { + "type": "string", + "pattern": "^$|^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$", + "minLength": 0, + "maxLength": 63, + "position": 2, + "description": "A full parent domain name. e.g. 'dash'." }, "normalizedParentDomainName": { "type": "string", - "pattern": "^$|^((?!-)[a-z0-9-\\.]{0,189}[a-z0-9])$", + "pattern": "^$|^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-\\.]{0,61}[a-hj-km-np-z0-9]$", "minLength": 0, - "maxLength": 190, - "description": "A full parent domain name in lowercase for case-insensitive uniqueness validation. e.g. 'dash'", - "$comment": "Must either be equal to an existing domain or empty to create a top level domain. Only the data contract owner can create top level domains." + "maxLength": 63, + "position": 3, + "description": "A parent domain name in lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'dash'", + "$comment": "Must either be equal to an existing domain or empty to create a top level domain. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\". Only the data contract owner can create top level domains." }, "preorderSalt": { "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32, + "position": 4, "description": "Salt used in the preorder document" }, "records": { "type": "object", "properties": { - "dashUniqueIdentityId": { - "type": "array", - "byteArray": true, - "minItems": 32, - "maxItems": 32, - "contentMediaType": "application/x.dash.dpp.identifier", - "description": "Identity ID to be used to create the primary name the Identity", - "$comment": "Must be equal to the document owner" - }, - "dashAliasIdentityId": { + "identity": { "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32, + "position": 1, "contentMediaType": "application/x.dash.dpp.identifier", - "description": "Identity ID to be used to create alias names for the Identity", - "$comment": "Must be equal to the document owner" + "description": "Identifier name record that refers to an Identity" } }, - "$comment": "Constraint with max and min properties ensure that only one identity record is used - either a `dashUniqueIdentityId` or a `dashAliasIdentityId`", "minProperties": 1, - "maxProperties": 1, + "position": 5, "additionalProperties": false }, "subdomainRules": { @@ -92,15 +104,22 @@ "allowSubdomains": { "type": "boolean", "description": "This option defines who can create subdomains: true - anyone; false - only the domain owner", - "$comment": "Only the domain owner is allowed to create subdomains for non top-level domains" + "$comment": "Only the domain owner is allowed to create subdomains for non top-level domains", + "position": 0 } }, + "position": 6, "description": "Subdomain rules allow domain owners to define rules for subdomains", "additionalProperties": false, - "required": ["allowSubdomains"] + "required": [ + "allowSubdomains" + ] } }, "required": [ + "$createdAt", + "$updatedAt", + "$transferredAt", "label", "normalizedLabel", "normalizedParentDomainName", @@ -108,12 +127,19 @@ "records", "subdomainRules" ], + "transient": [ + "preorderSalt" + ], "additionalProperties": false, "$comment": "In order to register a domain you need to create a preorder. The preorder step is needed to prevent man-in-the-middle attacks. normalizedLabel + '.' + normalizedParentDomain must not be longer than 253 chars length as defined by RFC 1035. Domain documents are immutable: modification and deletion are restricted" }, "preorder": { + "documentsMutable": false, + "canBeDeleted": true, + "type": "object", "indices": [ { + "name": "saltedHash", "properties": [ { "saltedDomainHash": "asc" @@ -128,6 +154,7 @@ "byteArray": true, "minItems": 32, "maxItems": 32, + "position": 0, "description": "Double sha-256 of the concatenation of a 32 byte random salt and a normalized domain name" } }, @@ -138,4 +165,4 @@ "$comment": "Preorder documents are immutable: modification and deletion are restricted" } } -} +} \ No newline at end of file From 76d6899602043ae00b25348e26f4dae5e42d31c8 Mon Sep 17 00:00:00 2001 From: pauldelucia Date: Sat, 24 Aug 2024 19:38:57 +0700 Subject: [PATCH 041/133] fix: change misspelling serivce to service --- .../Models/Managers/Chain Managers/DSMasternodeManager.m | 6 +++--- .../shared/Models/Masternode/DSMasternodeListDiffService.m | 2 +- DashSync/shared/Models/Masternode/DSMasternodeListService.h | 6 +++--- DashSync/shared/Models/Masternode/DSMasternodeListService.m | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m index 92cf5bd40..d63a89b47 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m @@ -104,15 +104,15 @@ - (instancetype)initWithChain:(DSChain *)chain { #pragma mark - DSMasternodeListServiceDelegate -- (DSMasternodeList *__nullable)masternodeListSerivceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { +- (DSMasternodeList *__nullable)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { return [self processRequestFromFileForBlockHash:blockHash]; } -- (void)masternodeListSerivceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { +- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { [self removeOutdatedMasternodeListsBeforeBlockHash:blockHash]; } -- (void)masternodeListSerivceEmptiedRetrievalQueue:(DSMasternodeListService *)service { +- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service { if (![self.masternodeListDiffService retrievalQueueCount]) { if (![self.quorumRotationService retrievalQueueCount]) [self removeOutdatedMasternodeListsBeforeBlockHash:self.store.lastQueriedBlockHash]; diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m index 8bb336468..d4cc93789 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m @@ -30,7 +30,7 @@ - (void)composeMasternodeListRequest:(NSOrderedSet *)list { //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 masternodeListSerivceDidRequestFileFromBlockHash:self blockHash:blockHash]; + DSMasternodeList *masternodeList = [self.delegate masternodeListServiceDidRequestFileFromBlockHash:self blockHash:blockHash]; if (masternodeList) { [self removeFromRetrievalQueue:blockHashData]; [self checkWaitingForQuorums]; diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.h b/DashSync/shared/Models/Masternode/DSMasternodeListService.h index ae1c225f8..bb7c4de38 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.h @@ -39,9 +39,9 @@ typedef NS_ENUM(NSUInteger, DSMasternodeListRequestMode) { @protocol DSMasternodeListServiceDelegate -- (DSMasternodeList *__nullable)masternodeListSerivceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; -- (void)masternodeListSerivceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; -- (void)masternodeListSerivceEmptiedRetrievalQueue:(DSMasternodeListService *)service; +- (DSMasternodeList *__nullable)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service; @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.m b/DashSync/shared/Models/Masternode/DSMasternodeListService.m index 3c8c15700..7c049949c 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.m @@ -312,7 +312,7 @@ - (void)updateMasternodeRetrievalQueue { - (void)fetchMasternodeListsToRetrieve:(void (^)(NSOrderedSet *listsToRetrieve))completion { if (![self retrievalQueueCount]) { DSLog(@"[%@] No masternode lists in retrieval: %@", self.chain.name, self); - [self.delegate masternodeListSerivceEmptiedRetrievalQueue:self]; + [self.delegate masternodeListServiceEmptiedRetrievalQueue:self]; return; } if ([self.requestsInRetrieval count]) { @@ -385,7 +385,7 @@ - (void)issueWithMasternodeListFromPeer:(DSPeer *)peer { //no need to remove local masternodes [self cleanListsRetrievalQueue]; [self.store deleteAllOnChain]; - [self.delegate masternodeListSerivceExceededMaxFailuresForMasternodeList:self blockHash:self.currentMasternodeList.blockHash]; + [self.delegate masternodeListServiceExceededMaxFailuresForMasternodeList:self blockHash:self.currentMasternodeList.blockHash]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS]; [self getRecentMasternodeList]; } else { From 545c440130ca0db9928d002315c4d53f83a23793 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 26 Aug 2024 17:28:04 +0700 Subject: [PATCH 042/133] removed DAPIGRPC --- DashSync.podspec | 1 - Example/DashSync.xcodeproj/project.pbxproj | 10 - Example/Podfile.lock | 667 +-------------------- Gemfile | 2 +- 4 files changed, 2 insertions(+), 678 deletions(-) diff --git a/DashSync.podspec b/DashSync.podspec index 98e8e450b..1dced5d7e 100644 --- a/DashSync.podspec +++ b/DashSync.podspec @@ -38,7 +38,6 @@ 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"' diff --git a/Example/DashSync.xcodeproj/project.pbxproj b/Example/DashSync.xcodeproj/project.pbxproj index c38885558..b87d8312c 100644 --- a/Example/DashSync.xcodeproj/project.pbxproj +++ b/Example/DashSync.xcodeproj/project.pbxproj @@ -2786,14 +2786,10 @@ 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; @@ -2848,14 +2844,10 @@ 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; @@ -3235,7 +3227,6 @@ "-l\"BoringSSL-GRPC-iOS\"", "-l\"CocoaImageHashing-iOS\"", "-l\"CocoaLumberjack-iOS\"", - "-l\"DAPI-GRPC-iOS\"", "-l\"DSDynamicOptions-iOS\"", "-l\"DWAlertController\"", "-l\"DashSync-iOS\"", @@ -3295,7 +3286,6 @@ "-l\"BoringSSL-GRPC-iOS\"", "-l\"CocoaImageHashing-iOS\"", "-l\"CocoaLumberjack-iOS\"", - "-l\"DAPI-GRPC-iOS\"", "-l\"DSDynamicOptions-iOS\"", "-l\"DWAlertController\"", "-l\"DashSync-iOS\"", diff --git a/Example/Podfile.lock b/Example/Podfile.lock index b0fb2e24d..27b0a3114 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,663 +1,18 @@ PODS: - - "!ProtoCompiler (3.21.5)": - - Protobuf (~> 3.0) - - "!ProtoCompiler-gRPCPlugin (1.49.0)": - - "!ProtoCompiler (= 3.21.5)" - - gRPC-ProtoRPC (= 1.49.0) - - abseil/algorithm/algorithm (1.20220623.0): - - abseil/base/config - - abseil/algorithm/container (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/base/atomic_hook (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/base (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/log_severity - - abseil/base/raw_logging_internal - - abseil/base/spinlock_wait - - abseil/meta/type_traits - - abseil/base/base_internal (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/base/config (1.20220623.0) - - abseil/base/core_headers (1.20220623.0): - - abseil/base/config - - abseil/base/dynamic_annotations (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver (1.20220623.0): - - abseil/base/config - - abseil/base/fast_type_id (1.20220623.0): - - abseil/base/config - - abseil/base/log_severity (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/malloc_internal (1.20220623.0): - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/base/prefetch (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/log_severity - - abseil/base/spinlock_wait (1.20220623.0): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/strerror (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/errno_saver - - abseil/base/throw_delegate (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/container/common (1.20220623.0): - - abseil/meta/type_traits - - abseil/types/optional - - abseil/container/compressed_tuple (1.20220623.0): - - abseil/utility/utility - - abseil/container/container_memory (1.20220623.0): - - abseil/base/config - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/container/fixed_array (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/throw_delegate - - abseil/container/compressed_tuple - - abseil/memory/memory - - abseil/container/flat_hash_map (1.20220623.0): - - abseil/algorithm/container - - abseil/base/core_headers - - abseil/container/container_memory - - abseil/container/hash_function_defaults - - abseil/container/raw_hash_map - - abseil/memory/memory - - abseil/container/flat_hash_set (1.20220623.0): - - abseil/algorithm/container - - abseil/base/core_headers - - abseil/container/container_memory - - abseil/container/hash_function_defaults - - abseil/container/raw_hash_set - - abseil/memory/memory - - abseil/container/hash_function_defaults (1.20220623.0): - - abseil/base/config - - abseil/hash/hash - - abseil/strings/cord - - abseil/strings/strings - - abseil/container/hash_policy_traits (1.20220623.0): - - abseil/meta/type_traits - - abseil/container/hashtable_debug_hooks (1.20220623.0): - - abseil/base/config - - abseil/container/hashtablez_sampler (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/stacktrace - - abseil/memory/memory - - abseil/profiling/exponential_biased - - abseil/profiling/sample_recorder - - abseil/synchronization/synchronization - - abseil/utility/utility - - abseil/container/inlined_vector (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/throw_delegate - - abseil/container/inlined_vector_internal - - abseil/memory/memory - - abseil/container/inlined_vector_internal (1.20220623.0): - - abseil/base/core_headers - - abseil/container/compressed_tuple - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/types/span - - abseil/container/layout (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/types/span - - abseil/utility/utility - - abseil/container/raw_hash_map (1.20220623.0): - - abseil/base/throw_delegate - - abseil/container/container_memory - - abseil/container/raw_hash_set - - abseil/container/raw_hash_set (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/prefetch - - abseil/container/common - - abseil/container/compressed_tuple - - abseil/container/container_memory - - abseil/container/hash_policy_traits - - abseil/container/hashtable_debug_hooks - - abseil/container/hashtablez_sampler - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/utility/utility - - abseil/debugging/debugging_internal (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/errno_saver - - abseil/base/raw_logging_internal - - abseil/debugging/demangle_internal (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/stacktrace (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/debugging/debugging_internal - - abseil/debugging/symbolize (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/debugging/debugging_internal - - abseil/debugging/demangle_internal - - abseil/strings/strings - - abseil/functional/any_invocable (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/functional/bind_front (1.20220623.0): - - abseil/base/base_internal - - abseil/container/compressed_tuple - - abseil/meta/type_traits - - abseil/utility/utility - - abseil/functional/function_ref (1.20220623.0): - - abseil/base/base_internal - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/hash/city (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/hash/hash (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/container/fixed_array - - abseil/functional/function_ref - - abseil/hash/city - - abseil/hash/low_level_hash - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/strings/strings - - abseil/types/optional - - abseil/types/variant - - abseil/utility/utility - - abseil/hash/low_level_hash (1.20220623.0): - - abseil/base/config - - abseil/base/endian - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/memory/memory (1.20220623.0): - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/meta/type_traits (1.20220623.0): - - abseil/base/config - - abseil/numeric/bits (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/int128 (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/bits - - abseil/numeric/representation (1.20220623.0): - - abseil/base/config - - abseil/profiling/exponential_biased (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/profiling/sample_recorder (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/synchronization/synchronization - - abseil/time/time - - abseil/random/distributions (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/random/internal/distribution_caller - - abseil/random/internal/fast_uniform_bits - - abseil/random/internal/fastmath - - abseil/random/internal/generate_real - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/traits - - abseil/random/internal/uniform_helper - - abseil/random/internal/wide_multiply - - abseil/strings/strings - - abseil/random/internal/distribution_caller (1.20220623.0): - - abseil/base/config - - abseil/base/fast_type_id - - abseil/utility/utility - - abseil/random/internal/fast_uniform_bits (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/random/internal/traits - - abseil/random/internal/fastmath (1.20220623.0): - - abseil/numeric/bits - - abseil/random/internal/generate_real (1.20220623.0): - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/random/internal/fastmath - - abseil/random/internal/traits - - abseil/random/internal/iostream_state_saver (1.20220623.0): - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/random/internal/nonsecure_base (1.20220623.0): - - abseil/base/core_headers - - abseil/container/inlined_vector - - abseil/meta/type_traits - - abseil/random/internal/pool_urbg - - abseil/random/internal/salted_seed_seq - - abseil/random/internal/seed_material - - abseil/types/span - - abseil/random/internal/pcg_engine (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/fastmath - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/platform (1.20220623.0): - - abseil/base/config - - abseil/random/internal/pool_urbg (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/random/internal/randen - - abseil/random/internal/seed_material - - abseil/random/internal/traits - - abseil/random/seed_gen_exception - - abseil/types/span - - abseil/random/internal/randen (1.20220623.0): - - abseil/base/raw_logging_internal - - abseil/random/internal/platform - - abseil/random/internal/randen_hwaes - - abseil/random/internal/randen_slow - - abseil/random/internal/randen_engine (1.20220623.0): - - abseil/base/endian - - abseil/meta/type_traits - - abseil/random/internal/iostream_state_saver - - abseil/random/internal/randen - - abseil/random/internal/randen_hwaes (1.20220623.0): - - abseil/base/config - - abseil/random/internal/platform - - abseil/random/internal/randen_hwaes_impl - - abseil/random/internal/randen_hwaes_impl (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/numeric/int128 - - abseil/random/internal/platform - - abseil/random/internal/randen_slow (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/numeric/int128 - - abseil/random/internal/platform - - abseil/random/internal/salted_seed_seq (1.20220623.0): - - abseil/container/inlined_vector - - abseil/meta/type_traits - - abseil/random/internal/seed_material - - abseil/types/optional - - abseil/types/span - - abseil/random/internal/seed_material (1.20220623.0): - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/raw_logging_internal - - abseil/random/internal/fast_uniform_bits - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/random/internal/traits (1.20220623.0): - - abseil/base/config - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/uniform_helper (1.20220623.0): - - abseil/base/config - - abseil/meta/type_traits - - abseil/numeric/int128 - - abseil/random/internal/traits - - abseil/random/internal/wide_multiply (1.20220623.0): - - abseil/base/config - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/random/internal/traits - - abseil/random/random (1.20220623.0): - - abseil/random/distributions - - abseil/random/internal/nonsecure_base - - abseil/random/internal/pcg_engine - - abseil/random/internal/pool_urbg - - abseil/random/internal/randen_engine - - abseil/random/seed_sequences - - abseil/random/seed_gen_exception (1.20220623.0): - - abseil/base/config - - abseil/random/seed_sequences (1.20220623.0): - - abseil/base/config - - abseil/random/internal/pool_urbg - - abseil/random/internal/salted_seed_seq - - abseil/random/internal/seed_material - - abseil/random/seed_gen_exception - - abseil/types/span - - abseil/status/status (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/base/strerror - - abseil/container/inlined_vector - - abseil/debugging/stacktrace - - abseil/debugging/symbolize - - abseil/functional/function_ref - - abseil/strings/cord - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/optional - - abseil/status/statusor (1.20220623.0): - - abseil/base/base - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/meta/type_traits - - abseil/status/status - - abseil/strings/strings - - abseil/types/variant - - abseil/utility/utility - - abseil/strings/cord (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/container/fixed_array - - abseil/container/inlined_vector - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/strings/cord_internal - - abseil/strings/cordz_functions - - abseil/strings/cordz_info - - abseil/strings/cordz_statistics - - abseil/strings/cordz_update_scope - - abseil/strings/cordz_update_tracker - - abseil/strings/internal - - abseil/strings/str_format - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/strings/cord_internal (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/base/throw_delegate - - abseil/container/compressed_tuple - - abseil/container/inlined_vector - - abseil/container/layout - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/strings/strings - - abseil/types/span - - abseil/strings/cordz_functions (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/profiling/exponential_biased - - abseil/strings/cordz_handle (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/synchronization/synchronization - - abseil/strings/cordz_info (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/container/inlined_vector - - abseil/debugging/stacktrace - - abseil/strings/cord_internal - - abseil/strings/cordz_functions - - abseil/strings/cordz_handle - - abseil/strings/cordz_statistics - - abseil/strings/cordz_update_tracker - - abseil/synchronization/synchronization - - abseil/types/span - - abseil/strings/cordz_statistics (1.20220623.0): - - abseil/base/config - - abseil/strings/cordz_update_tracker - - abseil/strings/cordz_update_scope (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/strings/cord_internal - - abseil/strings/cordz_info - - abseil/strings/cordz_update_tracker - - abseil/strings/cordz_update_tracker (1.20220623.0): - - abseil/base/config - - abseil/strings/internal (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/meta/type_traits - - abseil/strings/str_format (1.20220623.0): - - abseil/strings/str_format_internal - - abseil/strings/str_format_internal (1.20220623.0): - - abseil/base/config - - abseil/base/core_headers - - abseil/functional/function_ref - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/numeric/representation - - abseil/strings/strings - - abseil/types/optional - - abseil/types/span - - abseil/utility/utility - - abseil/strings/strings (1.20220623.0): - - abseil/base/base - - abseil/base/config - - abseil/base/core_headers - - abseil/base/endian - - abseil/base/raw_logging_internal - - abseil/base/throw_delegate - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/numeric/bits - - abseil/numeric/int128 - - abseil/strings/internal - - abseil/synchronization/graphcycles_internal (1.20220623.0): - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/synchronization/kernel_timeout_internal (1.20220623.0): - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/time/time - - abseil/synchronization/synchronization (1.20220623.0): - - abseil/base/atomic_hook - - abseil/base/base - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/base/dynamic_annotations - - abseil/base/malloc_internal - - abseil/base/raw_logging_internal - - abseil/debugging/stacktrace - - abseil/debugging/symbolize - - abseil/synchronization/graphcycles_internal - - abseil/synchronization/kernel_timeout_internal - - abseil/time/time - - abseil/time/internal/cctz/civil_time (1.20220623.0): - - abseil/base/config - - abseil/time/internal/cctz/time_zone (1.20220623.0): - - abseil/base/config - - abseil/time/internal/cctz/civil_time - - abseil/time/time (1.20220623.0): - - abseil/base/base - - abseil/base/core_headers - - abseil/base/raw_logging_internal - - abseil/numeric/int128 - - abseil/strings/strings - - abseil/time/internal/cctz/civil_time - - abseil/time/internal/cctz/time_zone - - abseil/types/bad_optional_access (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/types/bad_variant_access (1.20220623.0): - - abseil/base/config - - abseil/base/raw_logging_internal - - abseil/types/optional (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/memory/memory - - abseil/meta/type_traits - - abseil/types/bad_optional_access - - abseil/utility/utility - - abseil/types/span (1.20220623.0): - - abseil/algorithm/algorithm - - abseil/base/core_headers - - abseil/base/throw_delegate - - abseil/meta/type_traits - - abseil/types/variant (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/base/core_headers - - abseil/meta/type_traits - - abseil/types/bad_variant_access - - abseil/utility/utility - - abseil/utility/utility (1.20220623.0): - - abseil/base/base_internal - - abseil/base/config - - abseil/meta/type_traits - - BoringSSL-GRPC (0.0.24): - - BoringSSL-GRPC/Implementation (= 0.0.24) - - BoringSSL-GRPC/Interface (= 0.0.24) - - BoringSSL-GRPC/Implementation (0.0.24): - - BoringSSL-GRPC/Interface (= 0.0.24) - - BoringSSL-GRPC/Interface (0.0.24) - CocoaImageHashing (1.9.0) - CocoaLumberjack (3.7.2): - CocoaLumberjack/Core (= 3.7.2) - CocoaLumberjack/Core (3.7.2) - - DAPI-GRPC (0.22.0-dev.8): - - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - - DAPI-GRPC/Messages (= 0.22.0-dev.8) - - DAPI-GRPC/Services (= 0.22.0-dev.8) - - DAPI-GRPC/Messages (0.22.0-dev.8): - - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - - Protobuf - - DAPI-GRPC/Services (0.22.0-dev.8): - - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - - DAPI-GRPC/Messages - - gRPC-ProtoRPC - DashSharedCore (0.4.16) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - - DAPI-GRPC (= 0.22.0-dev.8) - DashSharedCore (= 0.4.16) - DSDynamicOptions (= 0.1.2) - DWAlertController (= 0.2.1) - TinyCborObjc (= 0.4.6) - DSDynamicOptions (0.1.2) - DWAlertController (0.2.1) - - gRPC-Core (1.49.0): - - gRPC-Core/Implementation (= 1.49.0) - - gRPC-Core/Interface (= 1.49.0) - - gRPC-Core/Implementation (1.49.0): - - abseil/base/base (= 1.20220623.0) - - abseil/base/core_headers (= 1.20220623.0) - - abseil/container/flat_hash_map (= 1.20220623.0) - - abseil/container/flat_hash_set (= 1.20220623.0) - - abseil/container/inlined_vector (= 1.20220623.0) - - abseil/functional/any_invocable (= 1.20220623.0) - - abseil/functional/bind_front (= 1.20220623.0) - - abseil/functional/function_ref (= 1.20220623.0) - - abseil/hash/hash (= 1.20220623.0) - - abseil/memory/memory (= 1.20220623.0) - - abseil/meta/type_traits (= 1.20220623.0) - - abseil/random/random (= 1.20220623.0) - - abseil/status/status (= 1.20220623.0) - - abseil/status/statusor (= 1.20220623.0) - - abseil/strings/cord (= 1.20220623.0) - - abseil/strings/str_format (= 1.20220623.0) - - abseil/strings/strings (= 1.20220623.0) - - abseil/synchronization/synchronization (= 1.20220623.0) - - abseil/time/time (= 1.20220623.0) - - abseil/types/optional (= 1.20220623.0) - - abseil/types/span (= 1.20220623.0) - - abseil/types/variant (= 1.20220623.0) - - abseil/utility/utility (= 1.20220623.0) - - BoringSSL-GRPC (= 0.0.24) - - gRPC-Core/Interface (= 1.49.0) - - gRPC-Core/Interface (1.49.0) - - gRPC-ProtoRPC (1.49.0): - - gRPC-ProtoRPC/Legacy (= 1.49.0) - - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) - - gRPC-ProtoRPC/Main (= 1.49.0) - - gRPC-ProtoRPC/Legacy (1.49.0): - - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) - - gRPC-ProtoRPC/Main (= 1.49.0) - - gRPC-RxLibrary (= 1.49.0) - - gRPC/GRPCCore (= 1.49.0) - - Protobuf (~> 3.0) - - gRPC-ProtoRPC/Legacy-Header (1.49.0) - - gRPC-ProtoRPC/Main (1.49.0): - - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) - - gRPC/Interface (= 1.49.0) - - Protobuf (~> 3.0) - - gRPC-RxLibrary (1.49.0): - - gRPC-RxLibrary/Implementation (= 1.49.0) - - gRPC-RxLibrary/Interface (= 1.49.0) - - gRPC-RxLibrary/Implementation (1.49.0): - - gRPC-RxLibrary/Interface - - gRPC-RxLibrary/Interface (1.49.0) - - gRPC/GRPCCore (1.49.0): - - gRPC-Core (= 1.49.0) - - gRPC-RxLibrary (= 1.49.0) - - gRPC/Interface (= 1.49.0) - - gRPC/Interface-Legacy (= 1.49.0) - - gRPC/Interface (1.49.0): - - gRPC/Interface-Legacy (= 1.49.0) - - gRPC/Interface-Legacy (1.49.0): - - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) - - Protobuf (3.27.2) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -673,21 +28,11 @@ DEPENDENCIES: SPEC REPOS: trunk: - - "!ProtoCompiler" - - "!ProtoCompiler-gRPCPlugin" - - abseil - - BoringSSL-GRPC - CocoaLumberjack - - DAPI-GRPC - DashSharedCore - DSDynamicOptions - DWAlertController - - gRPC - - gRPC-Core - - gRPC-ProtoRPC - - gRPC-RxLibrary - KVO-MVVM - - Protobuf - SDWebImage - tinycbor - TinyCborObjc @@ -705,23 +50,13 @@ CHECKOUT OPTIONS: :git: https://github.com/ameingast/cocoaimagehashing.git SPEC CHECKSUMS: - "!ProtoCompiler": e9c09244955a8565817aa59a4787b6bb849a63c6 - "!ProtoCompiler-gRPCPlugin": 755f0ee414a0d5f0028e0dcfe98c23bdbc3e6fa3 - abseil: 926fb7a82dc6d2b8e1f2ed7f3a718bce691d1e46 - BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33 CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da - DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b - DashSync: 5c4dea6e4ef83df33f23f85b7f2b97ef6843de87 + DashSync: dd9d652b73938fbef99137b3d60e12962ca915ea DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b - gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f - gRPC-Core: 3a9fdb5967d42211e875826f3f6fc163ea02c2a1 - gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d - gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2 SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 diff --git a/Gemfile b/Gemfile index a289656a4..2af168f6e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gem 'rexml' -gem 'cocoapods', '>=1.11.3' +gem 'cocoapods', '>=1.15.2' gem 'xcpretty' # ethon is manually updated to avoid segmentation fault crashes on the M1. See https://github.com/CocoaPods/CocoaPods/issues/10446 gem 'ethon', '>=0.13.0' From 7cf821f6845eadb37d481205ccc3055261317c56 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 26 Aug 2024 21:21:10 +0700 Subject: [PATCH 043/133] feat: event listeners --- .../Models/CoinJoin/DSCoinJoinManager.h | 16 +++++ .../Models/CoinJoin/DSCoinJoinManager.m | 41 ++++++++++-- .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 65 ++++++++++++++++--- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 11 ++-- DashSync/shared/Models/Network/DSPeer.m | 10 +-- DashSync/shared/Models/Wallet/DSAccount.h | 3 + DashSync/shared/Models/Wallet/DSAccount.m | 18 +++++ .../DashSync/DSTransactionsViewController.m | 62 +++++++++++++----- 9 files changed, 187 insertions(+), 41 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 10e2b1c98..47bbc76ff 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -26,11 +26,21 @@ NS_ASSUME_NONNULL_BEGIN +@protocol DSCoinJoinManagerDelegate + +- (void)sessionStartedWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)sessionCompleteWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)mixingCompleteWithStatuses:(NSArray *)statuses; +- (void)transactionProcessedWithId:(UInt256)txId type:(CoinJoinTransactionType)type; + +@end + @interface DSCoinJoinManager : NSObject @property (nonatomic, assign, nullable) DSChain *chain; @property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; +@property (nonatomic, readonly, weak) id managerDelegate; @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @@ -65,6 +75,12 @@ NS_ASSUME_NONNULL_BEGIN - (void)doAutomaticDenominating; - (void)updateSuccessBlock; - (BOOL)isWaitingForNewBlock; +- (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; + +- (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)onMixingComplete:(NSArray *)statuses; +- (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 76e07e189..2e3d25c2c 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -90,7 +90,6 @@ - (BOOL)isChainSynced { BOOL isSynced = self.chain.chainManager.isSynced; if (!isSynced) { - DSLog(@"[OBJ-C] CoinJoin: combinedSyncProgress: %f", self.chain.chainManager.syncState.combinedSyncProgress); [self.chain.chainManager startSync]; } @@ -191,7 +190,16 @@ - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { } - (void)handleTransactionReceivedNotification { - + DSWallet *wallet = self.chain.wallets.firstObject; + DSTransaction *lastTransaction = wallet.accounts.firstObject.recentTransactions.firstObject; + + if ([self.wrapper isMixingFeeTx:lastTransaction.txHash]) { + DSLog(@"[OBJ-C] CoinJoin tx: Mixing Fee: %@", uint256_reverse_hex(lastTransaction.txHash)); + [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_MixingFee]; + } else if ([self coinJoinTxTypeForTransaction:lastTransaction] == CoinJoinTransactionType_Mixing) { + DSLog(@"[OBJ-C] CoinJoin tx: Mixing Transaction: %@", uint256_reverse_hex(lastTransaction.txHash)); + [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; + } } - (void)doAutomaticDenominating { @@ -245,7 +253,6 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { } - (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress { - @synchronized(self) { // Note: cache is checked in dash-shared-core. @@ -522,9 +529,9 @@ - (Balance *)getBalance { NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; uint64_t anonymizedBalance = 0; uint64_t denominatedBalance = 0; - DSUTXO outpoint; NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; + for (NSValue *value in utxos) { [value getValue:&outpoint]; @@ -552,7 +559,6 @@ - (Balance *)getBalance { balance->my_trusted = self.chain.chainManager.chain.balance; balance->denominated_trusted = denominatedBalance; balance->anonymized = anonymizedBalance; - balance->my_immature = 0; balance->my_untrusted_pending = 0; balance->denominated_untrusted_pending = 0; @@ -709,9 +715,32 @@ - (CoinJoinClientOptions *)createOptions { options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; options->coinjoin_multi_session = YES; - DSLog(@"[OBJ-C] CoinJoin: trusted balance: %llu", self.chain.balance); return options; } +- (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { + return [self.wrapper coinJoinTxTypeForTransaction:transaction]; +} + +- (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { + DSLog(@"[OBJ-C] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; +} + +- (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { + DSLog(@"[OBJ-C] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; +} + +- (void)onMixingComplete:(nonnull NSArray *)statuses { + DSLog(@"[OBJ-C] CoinJoin: onMixingComplete: %@", statuses); + [self.managerDelegate mixingCompleteWithStatuses:statuses]; +} + +- (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { + DSLog(@"[OBJ-C] CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); + [self.managerDelegate transactionProcessedWithId:txId type:type]; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 832d09795..c9aa0307d 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -39,6 +39,8 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)doAutomaticDenominating; - (void)doMaintenance; - (void)registerCoinJoin:(CoinJoinClientOptions *)options; +- (BOOL)isMixingFeeTx:(UInt256)txId; +- (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 119d5e71d..783ed1bf5 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -44,7 +44,7 @@ - (void)registerCoinJoin:(CoinJoinClientOptions *)options { @synchronized (self) { if (_clientManager == NULL) { DSLog(@"[OBJ-C] CoinJoin: register client manager"); - _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionCompleteListener); + _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionLifecycleListener, mixingCompleteListener); DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); // TODO: add_wallet_ex @@ -127,6 +127,28 @@ - (void)notifyNewBestBlock:(DSBlock *)block { } } +- (BOOL)isMixingFeeTx:(UInt256)txId { + return is_mixing_fee_tx(_clientManager, (uint8_t (*)[32])(txId.u8)); +} + +- (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { + DSAccount *account = [self.chain firstAccountThatCanContainTransaction:transaction]; + NSArray *amountsSent = [account amountsSentByTransaction:transaction]; + + Transaction *tx = [transaction ffi_malloc:self.chain.chainType]; + uint64_t *inputValues = malloc(amountsSent.count * sizeof(uint64_t)); + + for (uintptr_t i = 0; i < amountsSent.count; i++) { + inputValues[i] = [amountsSent[i] unsignedLongLongValue]; + } + + CoinJoinTransactionType type = get_coinjoin_tx_type(tx, inputValues, amountsSent.count); + [DSTransaction ffi_free:tx]; + free(inputValues); + + return type; +} + - (DSChain *)chain { return self.chainManager.chain; } @@ -333,7 +355,6 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_d [scripts addObject:script]; } - // TODO: check subtract_fee_from_amount bool result = false; @synchronized (context) { @@ -342,13 +363,18 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_d if (error) { DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); } else if (is_denominating) { - DSLog(@"[OBJ-C] CoinJoin: Denominations Created: %@", uint256_reverse_hex(txId)); + DSLog(@"[OBJ-C] CoinJoin tx: Denominations Created: %@", uint256_reverse_hex(txId)); bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); + + if (!isFinished) { + DSLog(@"[OBJ-C] CoinJoin ERROR: auto_denom not finished"); + } + processor_destroy_block_hash(client_session_id); - DSLog(@"[OBJ-C] CoinJoin: is automatic_denominating finished: %s", isFinished ? "YES" : "NO"); + [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; } else { - DSLog(@"[OBJ-C] CoinJoin: Collateral Created: %@", uint256_reverse_hex(txId)); - // TODO: call listeners + DSLog(@"[OBJ-C] CoinJoin tx: Collateral Created: %@", uint256_reverse_hex(txId)); + [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } }]; } @@ -478,7 +504,7 @@ bool isWaitingForNewBlock(const void *context) { return result; } -void sessionCompleteListener(bool is_complete, +void sessionLifecycleListener(bool is_complete, int32_t base_session_id, uint8_t (*client_session_id)[32], uint32_t denomination, @@ -487,7 +513,30 @@ void sessionCompleteListener(bool is_complete, uint8_t (*ip_address)[16], bool joined, const void *context) { - // TODO + @synchronized (context) { + UInt256 clientSessionId = *((UInt256 *)client_session_id); + UInt128 ipAddress = *((UInt128 *)ip_address); + + if (is_complete) { + [AS_OBJC(context).manager onSessionComplete:base_session_id clientSessionId:clientSessionId denomination:denomination poolState:state poolMessage:message ipAddress:ipAddress isJoined:joined]; + } else { + [AS_OBJC(context).manager onSessionStarted:base_session_id clientSessionId:clientSessionId denomination:denomination poolState:state poolMessage:message ipAddress:ipAddress isJoined:joined]; + } + } +} + +void mixingCompleteListener(const enum PoolStatus *pool_statuses, + uintptr_t pool_statuses_len, + const void *context) { + @synchronized (context) { + NSMutableArray *statuses = [NSMutableArray array]; + + for (uintptr_t i = 0; i < pool_statuses_len; i++) { + [statuses addObject:@(pool_statuses[i])]; + } + + [AS_OBJC(context).manager onMixingComplete:statuses]; + } } @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index e833d645d..015732055 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -37,7 +37,7 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) DSChain *chain; -@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; // TODO: sync all access points +@property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; @property (nonatomic, strong) NSMutableSet *pendingSessions; @property (nonatomic, strong) NSMutableDictionary *masternodeMap; @property (nonatomic, strong) NSMutableDictionary *sessionMap; @@ -199,8 +199,8 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { @synchronized (self.inactives) { BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; - doDiscovery = !havPeersToTry; NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; + doDiscovery = !havPeersToTry || numPeers <= 0; DSPeer *peerToTry = nil; NSDate *retryTime = nil; @@ -344,7 +344,7 @@ - (void)updateMaxConnections { DSLog(@"[OBJ-C] CoinJoin peers: updateMaxConnections, pendingSessions.count: %lu", self.pendingSessions.count); _maxConnections = self.pendingSessions.count; NSUInteger connections = MIN(_maxConnections, DEFAULT_COINJOIN_SESSIONS); - DSLog(@"[OBJ-C] CoinJoin peers: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)connections); + DSLog(@"[OBJ-C] CoinJoin peers: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)DEFAULT_COINJOIN_SESSIONS); [self updateMaxConnections:connections]; } @@ -527,13 +527,11 @@ - (void)peerConnected:(nonnull DSPeer *)peer { } - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { - DSLog(@"[OBJ-C] CoinJoin peers: %@ disconnectedWithError %@", peer.location, error); - @synchronized (self) { [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; - DSLog(@"[OBJ-C] CoinJoin peers: Peer died: %@ (%lu connected, %lu pending, %lu max)", peer.location, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); + DSLog(@"[OBJ-C] CoinJoin peers: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; @@ -542,7 +540,6 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; if (numPeers < self.maxConnections) { - DSLog(@"[OBJ-C] CoinJoin peers: triggerConnections to get to maxConnections"); [self triggerConnections]; } } diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index c213d65ff..298393605 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -1823,26 +1823,26 @@ - (void)acceptGovObjectSyncMessage:(NSData *)message { - (void)acceptCoinJoinCompleteMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsc from %@", self.location); - [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_COMPLETE]; + [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_COMPLETE]; } - (void)acceptCoinJoinFinalTransaction:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dsf from %@", self.location); - [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_FINAL_TRANSACTION]; + [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_FINAL_TRANSACTION]; } - (void)acceptCoinJoinQueueMessage:(NSData *)message { - [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; + [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_QUEUE]; } - (void)acceptCoinJoinStatusUpdateMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dssu from %@", self.location); - [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_STATUS_UPDATE]; + [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_STATUS_UPDATE]; } - (void)acceptCoinJoinBroadcastTxMessage:(NSData *)message { DSLog(@"[OBJ-C] CoinJoin: got dstx from %@", self.location); - [[DSCoinJoinManager sharedInstanceForChain:_chain] processMessageFrom:self message:message type:MSG_COINJOIN_BROADCAST_TX]; + [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_BROADCAST_TX]; } // MARK: - hash diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index 2d16b37cf..fb43ca0c4 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -268,6 +268,9 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // retuns the amount sent from the account by the trasaction (total account outputs consumed, change and fee included) - (uint64_t)amountSentByTransaction:(DSTransaction *)transaction; +// Returns the amounts sent by the transaction +- (NSArray *)amountsSentByTransaction:(DSTransaction *)transaction; + // returns the external (receive) addresses of a transaction - (NSArray *)externalAddressesOfTransaction:(DSTransaction *)transaction; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 078f26e7c..99d93497d 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1672,6 +1672,24 @@ - (uint64_t)amountSentByTransaction:(DSTransaction *)transaction { return amount; } +// Returns the amounts sent by the transaction +- (NSArray *)amountsSentByTransaction:(DSTransaction *)transaction { + NSMutableArray *amounts = [NSMutableArray array]; + + for (DSTransactionInput *input in transaction.inputs) { + DSTransaction *tx = self.allTx[uint256_obj(input.inputHash)]; + uint64_t amount = tx.outputs[input.index].amount; + + if (amount > 0) { + [amounts addObject:@(amount)]; + } else { + [amounts addObject:@(0)]; + } + } + + return amounts; +} + // MARK: = Addresses - (NSArray *)externalAddressesOfTransaction:(DSTransaction *)transaction { diff --git a/Example/DashSync/DSTransactionsViewController.m b/Example/DashSync/DSTransactionsViewController.m index d71cff0c1..59ee24a93 100644 --- a/Example/DashSync/DSTransactionsViewController.m +++ b/Example/DashSync/DSTransactionsViewController.m @@ -9,6 +9,7 @@ #import "DSTransactionsViewController.h" #import "DSTransactionDetailViewController.h" #import "DSTransactionTableViewCell.h" +#import "DSCoinJoinManager.h" #import #import @@ -364,25 +365,56 @@ - (void)configureCell:(DSTransactionTableViewCell *)cell atIndexPath:(NSIndexPat cell.confirmationsLabel.hidden = YES; cell.directionLabel.hidden = NO; } + + CoinJoinTransactionType type = [[DSCoinJoinManager sharedInstanceForChain:self.chainManager.chain] coinJoinTxTypeForTransaction:tx]; + + if (type != CoinJoinTransactionType_None) { + NSString *typeString = @""; + + switch (type) { + case CoinJoinTransactionType_CreateDenomination: + typeString = @"Create Denomination"; + break; + case CoinJoinTransactionType_MakeCollateralInputs: + typeString = @"Make Collateral Inputs"; + break; + case CoinJoinTransactionType_MixingFee: + typeString = @"Mixing Fee"; + break; + case CoinJoinTransactionType_Mixing: + typeString = @"Mixing"; + break; + case CoinJoinTransactionType_Send: + typeString = @"Send"; + break; + default: + typeString = @"Unknown"; + break; + } - if (sent > 0 && received == sent) { + cell.directionLabel.text = NSLocalizedString(typeString, nil); cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:sent]; - cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", - [priceManager localCurrencyStringForDashAmount:sent]]; - cell.directionLabel.text = NSLocalizedString(@"moved", nil); cell.directionLabel.textColor = [UIColor blackColor]; - } else if (sent > 0) { - cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:received - sent]; - cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", - [priceManager localCurrencyStringForDashAmount:received - sent]]; - cell.directionLabel.text = NSLocalizedString(@"sent", nil); - cell.directionLabel.textColor = [UIColor colorWithRed:1.0 green:0.33 blue:0.33 alpha:1.0]; } else { - cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:received]; - cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", - [priceManager localCurrencyStringForDashAmount:received]]; - cell.directionLabel.text = NSLocalizedString(@"received", nil); - cell.directionLabel.textColor = [UIColor colorWithRed:0.0 green:0.75 blue:0.0 alpha:1.0]; + if (sent > 0 && received == sent) { + cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:sent]; + cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", + [priceManager localCurrencyStringForDashAmount:sent]]; + cell.directionLabel.text = NSLocalizedString(@"moved", nil); + cell.directionLabel.textColor = [UIColor blackColor]; + } else if (sent > 0) { + cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:received - sent]; + cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", + [priceManager localCurrencyStringForDashAmount:received - sent]]; + cell.directionLabel.text = NSLocalizedString(@"sent", nil); + cell.directionLabel.textColor = [UIColor colorWithRed:1.0 green:0.33 blue:0.33 alpha:1.0]; + } else { + cell.amountLabel.attributedText = [priceManager attributedStringForDashAmount:received]; + cell.fiatAmountLabel.text = [NSString stringWithFormat:@"(%@)", + [priceManager localCurrencyStringForDashAmount:received]]; + cell.directionLabel.text = NSLocalizedString(@"received", nil); + cell.directionLabel.textColor = [UIColor colorWithRed:0.0 green:0.75 blue:0.0 alpha:1.0]; + } } if (!cell.confirmationsLabel.hidden) { From 17841550add3b0ec80b4376c06727595821b5629 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 29 Aug 2024 15:14:56 +0700 Subject: [PATCH 044/133] fix: multisession mixing & refresh unused keys --- .../Models/CoinJoin/DSCoinJoinManager.h | 6 +- .../Models/CoinJoin/DSCoinJoinManager.m | 57 +++++--- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 122 ++++++++++++------ .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 1 - DashSync/shared/Models/Wallet/DSAccount.h | 6 + DashSync/shared/Models/Wallet/DSAccount.m | 8 ++ Example/DashSync/DSCoinJoinViewController.m | 17 +-- 8 files changed, 144 insertions(+), 74 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 47bbc76ff..38af0d915 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -30,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)sessionStartedWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)sessionCompleteWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)mixingStartedWithStatuses:(NSArray *)statuses; - (void)mixingCompleteWithStatuses:(NSArray *)statuses; - (void)transactionProcessedWithId:(UInt256)txId type:(CoinJoinTransactionType)type; @@ -56,6 +57,8 @@ NS_ASSUME_NONNULL_BEGIN - (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount; - (NSString *)freshAddress:(BOOL)internal; +- (NSArray *)getIssuedReceiveAddresses; +- (NSArray *)getUsedReceiveAddresses; - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished; - (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; - (uint64_t)validMNCount; @@ -74,11 +77,12 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)startMixing; - (void)doAutomaticDenominating; - (void)updateSuccessBlock; -- (BOOL)isWaitingForNewBlock; +- (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)onMixingStarted:(NSArray *)statuses; - (void)onMixingComplete:(NSArray *)statuses; - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 2e3d25c2c..b8a8c1acf 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -42,7 +42,6 @@ @interface DSCoinJoinManager () @property (nonatomic, strong) dispatch_queue_t processingQueue; @property (nonatomic, strong) dispatch_source_t coinjoinTimer; -@property (atomic) uint32_t lastSeenBlock; @property (atomic) int32_t cachedLastSuccessBlock; @property (atomic) int32_t cachedBlockHeight; // Keep track of current block height @@ -78,7 +77,6 @@ - (instancetype)initWithChain:(DSChain *)chain { _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chain.chainManager]; _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.coinjoin.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); - _lastSeenBlock = 0; _cachedBlockHeight = 0; _cachedLastSuccessBlock = 0; _options = [self createOptions]; @@ -86,6 +84,21 @@ - (instancetype)initWithChain:(DSChain *)chain { return self; } +- (CoinJoinClientOptions *)createOptions { + CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); + options->enable_coinjoin = YES; + options->coinjoin_rounds = 1; + options->coinjoin_sessions = 1; + options->coinjoin_amount = DUFFS / 8; // 0.125 DASH + options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; + options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; + options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; + options->coinjoin_multi_session = NO; + options->denom_only = NO; + + return options; +} + - (BOOL)isChainSynced { BOOL isSynced = self.chain.chainManager.isSynced; @@ -120,6 +133,7 @@ - (void)start { uint32_t delay = 1; @synchronized (self) { + self.cachedBlockHeight = self.chain.lastSyncBlock.height; [self.wrapper registerCoinJoin:self.options]; self.coinjoinTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.processingQueue); if (self.coinjoinTimer) { @@ -181,8 +195,8 @@ - (void)dealloc { } - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { - if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { - self.lastSeenBlock = self.chain.lastSyncBlock.height; + if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.cachedBlockHeight) { + self.cachedBlockHeight = self.chain.lastSyncBlock.height; dispatch_async(self.processingQueue, ^{ [self.wrapper notifyNewBestBlock:self.chain.lastSyncBlock]; }); @@ -227,6 +241,10 @@ - (void)setStopOnNothingToDo:(BOOL)stop { [self.wrapper setStopOnNothingToDo:stop]; } +- (void)refreshUnusedKeys { + [self.wrapper refreshUnusedKeys]; +} + - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { dispatch_async(self.processingQueue, ^{ if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { @@ -612,6 +630,16 @@ - (NSString *)freshAddress:(BOOL)internal { return address; } +- (NSArray *)getIssuedReceiveAddresses { + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + return account.allCoinJoinReceiveAddresses; +} + +- (NSArray *)getUsedReceiveAddresses { + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + return account.usedCoinJoinReceiveAddresses; +} + - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished { DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES]; @@ -628,7 +656,7 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output } else { [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { if (error) { - DSLog(@"[OBJ-C] CoinJoin publish error: %@", error.description); + DSLog(@"[OBJ-C] CoinJoin publish error: %@ for tx: %@", error.description, transaction.description); } else { DSLog(@"[OBJ-C] CoinJoin publish success: %@", transaction.description); } @@ -705,20 +733,6 @@ - (BOOL)isWaitingForNewBlock { return self.cachedBlockHeight - self.cachedLastSuccessBlock < MIN_BLOCKS_TO_WAIT; } -- (CoinJoinClientOptions *)createOptions { - CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); - options->enable_coinjoin = YES; - options->coinjoin_rounds = 1; - options->coinjoin_sessions = 1; - options->coinjoin_amount = DUFFS / 8; // 0.125 DASH - options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; - options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; - options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; - options->coinjoin_multi_session = YES; - - return options; -} - - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { return [self.wrapper coinJoinTxTypeForTransaction:transaction]; } @@ -733,6 +747,11 @@ - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId deno [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } +- (void)onMixingStarted:(nonnull NSArray *)statuses { + DSLog(@"[OBJ-C] CoinJoin: onMixingStarted: %@", statuses); + [self.managerDelegate mixingStartedWithStatuses:statuses]; +} + - (void)onMixingComplete:(nonnull NSArray *)statuses { DSLog(@"[OBJ-C] CoinJoin: onMixingComplete: %@", statuses); [self.managerDelegate mixingCompleteWithStatuses:statuses]; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index c9aa0307d..b292cb748 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)doMaintenance; - (void)registerCoinJoin:(CoinJoinClientOptions *)options; - (BOOL)isMixingFeeTx:(UInt256)txId; +- (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 783ed1bf5..a7dc36a0a 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -44,7 +44,7 @@ - (void)registerCoinJoin:(CoinJoinClientOptions *)options { @synchronized (self) { if (_clientManager == NULL) { DSLog(@"[OBJ-C] CoinJoin: register client manager"); - _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionLifecycleListener, mixingCompleteListener); + _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionLifecycleListener, mixingLifecycleListener, getCoinJoinKeys, destroyCoinJoinKeys); DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); // TODO: add_wallet_ex @@ -65,6 +65,12 @@ - (BOOL)startMixing { } } +- (void)refreshUnusedKeys { + @synchronized (self) { + refresh_unused_keys(self.clientManager); + } +} + - (BOOL)doAutomaticDenominating { @synchronized (self) { Balance *balance = [self.manager getBalance]; @@ -332,14 +338,18 @@ void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { } unsigned int countInputsWithAmount(unsigned long long inputAmount, const void *context) { - return [AS_OBJC(context).manager countInputsWithAmount:inputAmount]; + @synchronized (context) { + return [AS_OBJC(context).manager countInputsWithAmount:inputAmount]; + } } ByteArray freshCoinJoinAddress(bool internal, const void *context) { - DSCoinJoinWrapper *wrapper = AS_OBJC(context); - NSString *address = [wrapper.manager freshAddress:internal]; - - return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSString *address = [wrapper.manager freshAddress:internal]; + + return script_pubkey_for_address([address UTF8String], wrapper.chain.chainType); + } } bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_denominating, uint8_t (*client_session_id)[32], const void *context) { @@ -367,7 +377,7 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_d bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); if (!isFinished) { - DSLog(@"[OBJ-C] CoinJoin ERROR: auto_denom not finished"); + DSLog(@"[OBJ-C] CoinJoin: auto_denom not finished"); } processor_destroy_block_hash(client_session_id); @@ -402,25 +412,17 @@ void destroyMasternodeEntry(MasternodeEntry *masternodeEntry) { } uint64_t validMNCount(const void *context) { - uint64_t result = 0; - @synchronized (context) { - result = [AS_OBJC(context).manager validMNCount]; + return [AS_OBJC(context).manager validMNCount]; } - - return result; } MasternodeList* getMNList(const void *context) { - MasternodeList *masternodes; - @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSMasternodeList *mnList = [wrapper.manager mnList]; - masternodes = [mnList ffi_malloc]; + return [mnList ffi_malloc]; } - - return masternodes; } void destroyMNList(MasternodeList *masternodeList) { // TODO: check destroyMasternodeList @@ -432,54 +434,44 @@ void destroyMNList(MasternodeList *masternodeList) { // TODO: check destroyMaste } bool isBlockchainSynced(const void *context) { - return AS_OBJC(context).manager.isChainSynced; + @synchronized (context) { + return AS_OBJC(context).manager.isChainSynced; + } } bool isMasternodeOrDisconnectRequested(uint8_t (*ip_address)[16], uint16_t port, const void *context) { UInt128 ipAddress = *((UInt128 *)ip_address); - BOOL result = NO; @synchronized (context) { - result = [AS_OBJC(context).manager isMasternodeOrDisconnectRequested:ipAddress port:port]; + return [AS_OBJC(context).manager isMasternodeOrDisconnectRequested:ipAddress port:port]; } - - return result; } bool disconnectMasternode(uint8_t (*ip_address)[16], uint16_t port, const void *context) { UInt128 ipAddress = *((UInt128 *)ip_address); - BOOL result = NO; @synchronized (context) { - result = [AS_OBJC(context).manager disconnectMasternode:ipAddress port:port]; + return [AS_OBJC(context).manager disconnectMasternode:ipAddress port:port]; } - - return result; } bool sendMessage(char *message_type, ByteArray *byteArray, uint8_t (*ip_address)[16], uint16_t port, bool warn, const void *context) { NSString *messageType = [NSString stringWithUTF8String:message_type]; UInt128 ipAddress = *((UInt128 *)ip_address); - BOOL result = YES; @synchronized (context) { NSData *message = [NSData dataWithBytes:byteArray->ptr length:byteArray->len]; - result = [AS_OBJC(context).manager sendMessageOfType:messageType message:message withPeerIP:ipAddress port:port warn:warn]; + return [AS_OBJC(context).manager sendMessageOfType:messageType message:message withPeerIP:ipAddress port:port warn:warn]; } - - return result; } bool addPendingMasternode(uint8_t (*pro_tx_hash)[32], uint8_t (*session_id)[32], const void *context) { UInt256 sessionId = *((UInt256 *)session_id); UInt256 proTxHash = *((UInt256 *)pro_tx_hash); - BOOL result = NO; @synchronized (context) { - result = [AS_OBJC(context).manager addPendingMasternode:proTxHash clientSessionId:sessionId]; + return [AS_OBJC(context).manager addPendingMasternode:proTxHash clientSessionId:sessionId]; } - - return result; } void startManagerAsync(const void *context) { @@ -495,13 +487,9 @@ void updateSuccessBlock(const void *context) { } bool isWaitingForNewBlock(const void *context) { - BOOL result = NO; - @synchronized (context) { - result = [AS_OBJC(context).manager isWaitingForNewBlock]; + return [AS_OBJC(context).manager isWaitingForNewBlock]; } - - return result; } void sessionLifecycleListener(bool is_complete, @@ -525,9 +513,10 @@ void sessionLifecycleListener(bool is_complete, } } -void mixingCompleteListener(const enum PoolStatus *pool_statuses, - uintptr_t pool_statuses_len, - const void *context) { +void mixingLifecycleListener(bool is_complete, + const enum PoolStatus *pool_statuses, + uintptr_t pool_statuses_len, + const void *context) { @synchronized (context) { NSMutableArray *statuses = [NSMutableArray array]; @@ -535,8 +524,55 @@ void mixingCompleteListener(const enum PoolStatus *pool_statuses, [statuses addObject:@(pool_statuses[i])]; } - [AS_OBJC(context).manager onMixingComplete:statuses]; + if (is_complete) { + [AS_OBJC(context).manager onMixingComplete:statuses]; + } else { + [AS_OBJC(context).manager onMixingStarted:statuses]; + } + } +} + +CoinJoinKeys* getCoinJoinKeys(bool used, const void *context) { + @synchronized (context) { + DSCoinJoinWrapper *wrapper = AS_OBJC(context); + NSArray *addresses; + + if (used) { + addresses = [wrapper.manager getIssuedReceiveAddresses]; + } else { + addresses = [wrapper.manager getUsedReceiveAddresses]; + } + + CoinJoinKeys *keys = malloc(sizeof(CoinJoinKeys)); + keys->item_count = addresses.count; + keys->items = malloc(sizeof(ByteArray *) * keys->item_count); + + for (NSUInteger i = 0; i < addresses.count; i++) { + NSString *address = addresses[i]; + ByteArray *byteArray = malloc(sizeof(ByteArray)); + byteArray->ptr = script_pubkey_for_address([address UTF8String], wrapper.chain.chainType).ptr; + byteArray->len = script_pubkey_for_address([address UTF8String], wrapper.chain.chainType).len; + keys->items[i] = byteArray; + } + + return keys; + } +} + +void destroyCoinJoinKeys(struct CoinJoinKeys *coinjoin_keys) { + if (coinjoin_keys == NULL) { + return; } + + for (uintptr_t i = 0; i < coinjoin_keys->item_count; i++) { + if (coinjoin_keys->items[i] != NULL) { + free(coinjoin_keys->items[i]->ptr); + free(coinjoin_keys->items[i]); + } + } + + free(coinjoin_keys->items); + free(coinjoin_keys); } @end diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 015732055..ed44dc49a 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -48,7 +48,6 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) NSMutableSet *mutablePendingPeers; @property (nonatomic, readonly) BOOL shouldSendDsq; @property (nullable, nonatomic, readwrite) DSPeer *downloadPeer; -@property (nonatomic, readonly) NSUInteger backoff; @property (nonatomic, strong) DSBackoff *groupBackoff; @property (nonatomic, strong) NSMutableDictionary *backoffMap; @property (nonatomic, strong) NSMutableArray *inactives; diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index fb43ca0c4..f9bc6138f 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -102,6 +102,12 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // returns the first unused coinjoin internal address @property (nullable, nonatomic, readonly) NSString *coinJoinChangeAddress; +// returns all issued CoinJoin receive addresses +@property (nullable, nonatomic, readonly) NSArray *usedCoinJoinReceiveAddresses; + +// returns all used CoinJoin receive addresses +@property (nullable, nonatomic, readonly) NSArray *allCoinJoinReceiveAddresses; + // all the contacts for an account @property (nonatomic, readonly) NSArray *_Nonnull contacts; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 99d93497d..4ef40c0d6 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -360,6 +360,14 @@ - (NSString *)coinJoinChangeAddress { return address; } +- (NSArray *)allCoinJoinReceiveAddresses { + return [self.coinJoinDerivationPath allReceiveAddresses]; +} + +- (NSArray *)usedCoinJoinReceiveAddresses { + return [self.coinJoinDerivationPath usedReceiveAddresses]; +} + // NSData objects containing serialized UTXOs - (NSArray *)unspentOutputs { return self.utxos.array; diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index ef44c70b8..c454349e4 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -42,22 +42,19 @@ - (void)viewDidDisappear:(BOOL)animated { } - (void)startCoinJoin { - // TODO: subscribe - // TODO: refreshUnusedKeys() - - if (_coinJoinManager == NULL) { - _coinJoinManager = [DSCoinJoinManager sharedInstanceForChain:_chainManager.chain]; + if (self.coinJoinManager == NULL) { + self.coinJoinManager = [DSCoinJoinManager sharedInstanceForChain:self.chainManager.chain]; } - [_coinJoinManager start]; -// wallet.getCoinJoin().refreshUnusedKeys(); TODO - [_coinJoinManager setStopOnNothingToDo:true]; + [self.coinJoinManager start]; + [self.coinJoinManager refreshUnusedKeys]; + [self.coinJoinManager setStopOnNothingToDo:true]; - if (![_coinJoinManager startMixing]) { + if (![self.coinJoinManager startMixing]) { DSLog(@"[OBJ-C] CoinJoin: Mixing has been started already."); } - [_coinJoinManager doAutomaticDenominating]; + [self.coinJoinManager doAutomaticDenominating]; } @end From f1bab5b0d87038f8f96925b8f15d731bda388825 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sat, 31 Aug 2024 21:28:05 +0700 Subject: [PATCH 045/133] fix: skip registerAddressesWithGapLimit if addresses aren't loaded --- .../shared/Models/Derivation Paths/DSFundsDerivationPath.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m index a6389be0a..936eefb5f 100644 --- a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m @@ -140,9 +140,10 @@ - (BOOL)registerTransactionAddress:(NSString *_Nonnull)address { // 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 internal:(BOOL)internal error:(NSError **)error { - if (!self.account.wallet.isTransient) { - NSAssert(self.addressesLoaded, @"addresses must be loaded before calling this function"); + if (!self.account.wallet.isTransient && !self.addressesLoaded) { + return @[]; } + @synchronized(self) { NSMutableArray *a = [NSMutableArray arrayWithArray:(internal) ? self.internalAddresses : self.externalAddresses]; NSUInteger i = a.count; From 0d5175ac0473e8e788748974575584f84e0e39c1 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 4 Sep 2024 19:51:31 +0700 Subject: [PATCH 046/133] chore: wallet integration changes --- .../Models/CoinJoin/DSCoinJoinBalance.h | 50 +++++ .../Models/CoinJoin/DSCoinJoinBalance.m | 66 ++++++ .../Models/CoinJoin/DSCoinJoinManager.h | 22 +- .../Models/CoinJoin/DSCoinJoinManager.m | 211 ++++++++++++++++-- .../Models/CoinJoin/DSCoinJoinWrapper.h | 13 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 97 +++++++- .../Models/CoinJoin/{ => Utils}/DSBackoff.h | 0 .../Models/CoinJoin/{ => Utils}/DSBackoff.m | 0 DashSync/shared/Models/Wallet/DSAccount.h | 3 + DashSync/shared/Models/Wallet/DSAccount.m | 13 ++ Example/DashSync/DSCoinJoinViewController.m | 3 +- 11 files changed, 441 insertions(+), 37 deletions(-) create mode 100644 DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.h create mode 100644 DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.m rename DashSync/shared/Models/CoinJoin/{ => Utils}/DSBackoff.h (100%) rename DashSync/shared/Models/CoinJoin/{ => Utils}/DSBackoff.m (100%) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.h new file mode 100644 index 000000000..2bff0c3fe --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.h @@ -0,0 +1,50 @@ +// +// Created by Andrei Ashikhmin +// 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" + +NS_ASSUME_NONNULL_BEGIN + +@interface DSCoinJoinBalance : NSObject + +@property (nonatomic, assign) uint64_t myTrusted; +@property (nonatomic, assign) uint64_t denominatedTrusted; +@property (nonatomic, assign) uint64_t anonymized; +@property (nonatomic, assign) uint64_t myImmature; +@property (nonatomic, assign) uint64_t myUntrustedPending; +@property (nonatomic, assign) uint64_t denominatedUntrustedPending; +@property (nonatomic, assign) uint64_t watchOnlyTrusted; +@property (nonatomic, assign) uint64_t watchOnlyUntrustedPending; +@property (nonatomic, assign) uint64_t watchOnlyImmature; + ++ (DSCoinJoinBalance *)balanceWithMyTrusted:(uint64_t)myTrusted + denominatedTrusted:(uint64_t)denominatedTrusted + anonymized:(uint64_t)anonymized + myImmature:(uint64_t)myImmature + myUntrustedPending:(uint64_t)myUntrustedPending + denominatedUntrustedPending:(uint64_t)denominatedUntrustedPending + watchOnlyTrusted:(uint64_t)watchOnlyTrusted + watchOnlyUntrustedPending:(uint64_t)watchOnlyUntrustedPending + watchOnlyImmature:(uint64_t)watchOnlyImmature; + ++ (void)ffi_free:(Balance *)balance; +- (Balance *)ffi_malloc; + +@end + +NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.m new file mode 100644 index 000000000..fbf2f12bf --- /dev/null +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinBalance.m @@ -0,0 +1,66 @@ +// +// Created by Andrei Ashikhmin +// 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 "DSCoinJoinBalance.h" + +@implementation DSCoinJoinBalance + ++ (DSCoinJoinBalance *)balanceWithMyTrusted:(uint64_t)myTrusted + denominatedTrusted:(uint64_t)denominatedTrusted + anonymized:(uint64_t)anonymized + myImmature:(uint64_t)myImmature + myUntrustedPending:(uint64_t)myUntrustedPending + denominatedUntrustedPending:(uint64_t)denominatedUntrustedPending + watchOnlyTrusted:(uint64_t)watchOnlyTrusted + watchOnlyUntrustedPending:(uint64_t)watchOnlyUntrustedPending + watchOnlyImmature:(uint64_t)watchOnlyImmature { + + DSCoinJoinBalance *balance = [[DSCoinJoinBalance alloc] init]; + balance.myTrusted = myTrusted; + balance.denominatedTrusted = denominatedTrusted; + balance.anonymized = anonymized; + balance.myImmature = myImmature; + balance.myUntrustedPending = myUntrustedPending; + balance.denominatedUntrustedPending = denominatedUntrustedPending; + balance.watchOnlyTrusted = watchOnlyTrusted; + balance.watchOnlyUntrustedPending = watchOnlyUntrustedPending; + balance.watchOnlyImmature = watchOnlyImmature; + + return balance; +} + +- (Balance *)ffi_malloc { + Balance *balance = malloc(sizeof(Balance)); + balance->my_trusted = self.myTrusted; + balance->denominated_trusted = self.denominatedTrusted; + balance->anonymized = self.anonymized; + balance->my_immature = self.myImmature; + balance->my_untrusted_pending = self.myUntrustedPending; + balance->denominated_untrusted_pending = self.denominatedUntrustedPending; + balance->watch_only_trusted = self.watchOnlyTrusted; + balance->watch_only_untrusted_pending = self.watchOnlyUntrustedPending; + balance->watch_only_immature = self.watchOnlyImmature; + + return balance; +} + ++ (void)ffi_free:(Balance *)balance { + if (balance) { + free(balance); + } +} +@end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 38af0d915..40dc51ab7 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -23,6 +23,7 @@ #import "DSCoinJoinManager.h" #import "DSCoinJoinWrapper.h" #import "DSMasternodeGroup.h" +#import "DSCoinJoinBalance.h" NS_ASSUME_NONNULL_BEGIN @@ -30,8 +31,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)sessionStartedWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)sessionCompleteWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; -- (void)mixingStartedWithStatuses:(NSArray *)statuses; -- (void)mixingCompleteWithStatuses:(NSArray *)statuses; +- (void)mixingStarted; +- (void)mixingComplete:(BOOL)withError; - (void)transactionProcessedWithId:(UInt256)txId type:(CoinJoinTransactionType)type; @end @@ -41,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, nullable) DSChain *chain; @property (nonatomic, strong, nullable) DSMasternodeGroup *masternodeGroup; @property (nonatomic, assign, nullable) CoinJoinClientOptions *options; -@property (nonatomic, readonly, weak) id managerDelegate; +@property (nonatomic, nullable, weak) id managerDelegate; @property (nonatomic, assign) BOOL anonymizableTallyCachedNonDenom; @property (nonatomic, assign) BOOL anonymizableTallyCached; @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @@ -52,6 +53,7 @@ NS_ASSUME_NONNULL_BEGIN + (instancetype)sharedInstanceForChain:(DSChain *)chain; - (instancetype)initWithChain:(DSChain *)chain; +- (void)initMasternodeGroup; - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index; - (NSArray *) availableCoins:(WalletEx *)walletEx onlySafe:(BOOL)onlySafe coinControl:(DSCoinControl *_Nullable)coinControl minimumAmount:(uint64_t)minimumAmount maximumAmount:(uint64_t)maximumAmount minimumSumAmount:(uint64_t)minimumSumAmount maximumCount:(uint64_t)maximumCount; - (NSArray *)selectCoinsGroupedByAddresses:(WalletEx *)walletEx skipDenominated:(BOOL)skipDenominated anonymizable:(BOOL)anonymizable skipUnconfirmed:(BOOL)skipUnconfirmed maxOupointsPerAddress:(int32_t)maxOupointsPerAddress; @@ -66,20 +68,30 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port; - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port; - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port warn:(BOOL)warn; -- (Balance *)getBalance; - +- (DSCoinJoinBalance *)getBalance; +- (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessions:(int32_t)sessions withMultisession:(BOOL)multisession denominationGoal:(int32_t)denomGoal denominationHardCap:(int32_t)denomHardCap; - (void)startAsync; - (void)stopAsync; - (void)start; +- (void)stop; - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId; - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; - (void)doAutomaticDenominating; +- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun; - (void)updateSuccessBlock; - (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; +- (double)getMixingProgress; + +- (uint64_t)getSmallestDenomination; +- (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; +- (void)updateOptionsWithAmount:(uint64_t)amount; +- (void)updateOptionsWithEnabled:(BOOL)isEnabled; +- (int32_t)getActiveSessionCount; +// Events - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)onMixingStarted:(NSArray *)statuses; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index b8a8c1acf..5db9e7754 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -33,6 +33,7 @@ #import "DSSimplifiedMasternodeEntry.h" #import "DSChain+Protected.h" #import "DSBlock.h" +#import "DSKeyManager.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @@ -75,7 +76,6 @@ - (instancetype)initWithChain:(DSChain *)chain { if (self) { _chain = chain; _wrapper = [[DSCoinJoinWrapper alloc] initWithManagers:self chainManager:chain.chainManager]; - _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.coinjoin.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); _cachedBlockHeight = 0; _cachedLastSuccessBlock = 0; @@ -84,12 +84,16 @@ - (instancetype)initWithChain:(DSChain *)chain { return self; } +- (void)initMasternodeGroup { + _masternodeGroup = [[DSMasternodeGroup alloc] initWithManager:self]; +} + - (CoinJoinClientOptions *)createOptions { CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); options->enable_coinjoin = YES; options->coinjoin_rounds = 1; options->coinjoin_sessions = 1; - options->coinjoin_amount = DUFFS / 8; // 0.125 DASH + options->coinjoin_amount = DUFFS / 8; options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; @@ -99,6 +103,36 @@ - (CoinJoinClientOptions *)createOptions { return options; } +- (void)updateOptionsWithAmount:(uint64_t)amount { + self.options->coinjoin_amount = amount; + + if (self.wrapper.isRegistered) { + [self.wrapper updateOptions:self.options]; + } +} + +- (void)updateOptionsWithEnabled:(BOOL)isEnabled { + self.options->enable_coinjoin = isEnabled; + + if (self.wrapper.isRegistered) { + [self.wrapper updateOptions:self.options]; + } +} + +- (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessions:(int32_t)sessions withMultisession:(BOOL)multisession denominationGoal:(int32_t)denomGoal denominationHardCap:(int32_t)denomHardCap { + DSLog(@"[OBJ-C] CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); + self.options->coinjoin_amount = amount; + self.options->coinjoin_rounds = rounds; + self.options->coinjoin_sessions = sessions; + self.options->coinjoin_multi_session = multisession; + self.options->coinjoin_denoms_goal = denomGoal; + self.options->coinjoin_denoms_hardcap = denomHardCap; + + if (self.wrapper.isRegistered) { + [self.wrapper updateOptions:self.options]; + } +} + - (BOOL)isChainSynced { BOOL isSynced = self.chain.chainManager.isSynced; @@ -165,6 +199,7 @@ - (void)doMaintenance { } - (BOOL)startMixing { + DSLog(@"[OBJ-C] CoinJoin: mixing progress: %f", [self getMixingProgress]); self.isMixing = true; return [self.wrapper startMixing]; } @@ -173,7 +208,8 @@ - (void)stop { DSLog(@"[OBJ-C] CoinJoinManager stopping"); [self cancelCoinjoinTimer]; self.isMixing = false; -// [self.wrapper stopAndResetClientManager]; TODO + self.cachedLastSuccessBlock = 0; + [self.wrapper stopAndResetClientManager]; [self stopAsync]; } @@ -217,15 +253,29 @@ - (void)handleTransactionReceivedNotification { } - (void)doAutomaticDenominating { - if ([self validMNCount] == 0) { - DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); + if ([self validMNCount] == 0) { + DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); return; - } - + } + dispatch_async(self.processingQueue, ^{ DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); - [self.wrapper doAutomaticDenominating]; + [self.wrapper doAutomaticDenominatingWithDryRun:NO]; }); + } + +- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { + if ([self validMNCount] == 0) { + DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); + return false; + } + + if (![self.wrapper isRegistered]) { + [self.wrapper registerCoinJoin:_options]; + } + + DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); + return [self.wrapper doAutomaticDenominatingWithDryRun:dryRun]; } - (void)cancelCoinjoinTimer { @@ -531,6 +581,84 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { return vCoins; } +- (double)getMixingProgress { + double requiredRounds = self.options->coinjoin_rounds + 0.875; // 1 x 50% + 1 x 50%^2 + 1 x 50%^3 + __block int totalInputs = 0; + __block int totalRounds = 0; + + NSDictionary *> *outputs = [self getOutputs]; + uint64_t collateralAmount = [self.wrapper getCollateralAmount]; + NSArray *denominations = [self.wrapper getStandardDenominations]; + + [outputs enumerateKeysAndObjectsUsingBlock:^(NSNumber *denom, NSArray *outputs, BOOL *stop) { + [outputs enumerateObjectsUsingBlock:^(NSValue *output, NSUInteger idx, BOOL *stop) { + DSUTXO outpoint; + [output getValue:&outpoint]; + + if (denom.intValue >= 0) { + int rounds = [self.wrapper getRealOutpointCoinJoinRounds:outpoint]; + + if (rounds >= 0) { + totalInputs += 1; + totalRounds += rounds; + } + } else if (denom.intValue == -2) { + DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; + DSTransactionOutput *output = tx.outputs[outpoint.n]; + + __block int unmixedInputs = 0; + __block uint64_t outputValue = output.amount - collateralAmount; + + [denominations enumerateObjectsUsingBlock:^(NSNumber *coin, NSUInteger idx, BOOL *stop) { + while (outputValue > coin.unsignedLongLongValue) { + unmixedInputs++; + outputValue -= coin.unsignedLongLongValue; + } + }]; + + totalInputs += unmixedInputs; + } + }]; + }]; + + double progress = totalInputs != 0 ? (double)totalRounds / (requiredRounds * totalInputs) : 0.0; + DSLog(@"[OBJ-C] CoinJoin: getMixingProgress: %f = %d / (%f * %d)", progress, totalRounds, requiredRounds, totalInputs); + return fmax(0.0, fmin(progress, 1.0)); +} + +- (NSDictionary *> *)getOutputs { + NSMutableDictionary *> *outputs = [NSMutableDictionary dictionary]; + + for (NSNumber *amount in [self.wrapper getStandardDenominations]) { + outputs[@([self.wrapper amountToDenomination:amount.unsignedLongLongValue])] = [NSMutableArray array]; + } + + outputs[@(-2)] = [NSMutableArray array]; + outputs[@(0)] = [NSMutableArray array]; + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + NSArray *utxos = account.unspentOutputs; + DSUTXO outpoint; + + for (NSValue *value in utxos) { + [value getValue:&outpoint]; + + DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; + DSTransactionOutput *output = tx.outputs[outpoint.n]; + NSString *address = [DSKeyManager addressWithScriptPubKey:output.outScript forChain:self.chain]; + + if ([account containsCoinJoinAddress:address]) { + int denom = [self.wrapper amountToDenomination:output.amount]; + NSMutableArray *listDenoms = outputs[@(denom)]; + [listDenoms addObject:value]; + } else { + // non-denominated and non-collateral coins + [outputs[@(-2)] addObject:value]; + } + } + + return outputs; +} + - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { if (!is_denominated_amount(output.amount)) { return false; @@ -543,7 +671,7 @@ - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { return [self.chain.wallets.firstObject.accounts.firstObject.coinJoinDerivationPath containsAddress:output.address]; } -- (Balance *)getBalance { +- (DSCoinJoinBalance *)getBalance { NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; uint64_t anonymizedBalance = 0; uint64_t denominatedBalance = 0; @@ -572,17 +700,18 @@ - (Balance *)getBalance { } } } - - Balance *balance = malloc(sizeof(Balance)); - balance->my_trusted = self.chain.chainManager.chain.balance; - balance->denominated_trusted = denominatedBalance; - balance->anonymized = anonymizedBalance; - balance->my_immature = 0; - balance->my_untrusted_pending = 0; - balance->denominated_untrusted_pending = 0; - balance->watch_only_trusted = 0; - balance->watch_only_untrusted_pending = 0; - balance->watch_only_immature = 0; + + // TODO: support more balance types? + DSCoinJoinBalance *balance = + [DSCoinJoinBalance balanceWithMyTrusted:self.chain.balance + denominatedTrusted:denominatedBalance + anonymized:anonymizedBalance + myImmature:0 + myUntrustedPending:0 + denominatedUntrustedPending:0 + watchOnlyTrusted:0 + watchOnlyUntrustedPending:0 + watchOnlyImmature:0]; return balance; } @@ -737,6 +866,29 @@ - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transac return [self.wrapper coinJoinTxTypeForTransaction:transaction]; } +- (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed { + return [self.wrapper getAnonymizableBalance:skipDenominated skipUnconfirmed:skipUnconfirmed]; +} + +- (uint64_t)getSmallestDenomination { + return [self.wrapper getSmallestDenomination]; +} + +- (int32_t)getActiveSessionCount { + int32_t result = 0; + NSArray *statuses = [self.wrapper getSessionStatuses]; + + for (NSNumber *status in statuses) { + if (status == PoolStatus_Connecting || status == PoolStatus_Connected || status == PoolStatus_Mixing) { + result += 1; + } + } + + return result; +} + +// Events + - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { DSLog(@"[OBJ-C] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; @@ -744,21 +896,36 @@ - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denom - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { DSLog(@"[OBJ-C] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"[OBJ-C] CoinJoin: mixing progress: %f", [self getMixingProgress]); [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onMixingStarted:(nonnull NSArray *)statuses { DSLog(@"[OBJ-C] CoinJoin: onMixingStarted: %@", statuses); - [self.managerDelegate mixingStartedWithStatuses:statuses]; + [self.managerDelegate mixingStarted]; } - (void)onMixingComplete:(nonnull NSArray *)statuses { DSLog(@"[OBJ-C] CoinJoin: onMixingComplete: %@", statuses); - [self.managerDelegate mixingCompleteWithStatuses:statuses]; + + BOOL isError = NO; + for (NSNumber *statusNumber in statuses) { + PoolStatus status = [statusNumber intValue]; + if (status != PoolStatus_Finished && + status != PoolStatus_ErrNotEnoughFunds && + status != PoolStatus_ErrNoInputs) { + isError = YES; + DSLog(@"[OBJ-C] CoinJoin: Mixing stopped before completion. Status: %d", status); + break; + } + } + + [self.managerDelegate mixingComplete:isError]; } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { DSLog(@"[OBJ-C] CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); + DSLog(@"[OBJ-C] CoinJoin: mixing progress: %f", [self getMixingProgress]); [self.managerDelegate transactionProcessedWithId:txId type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index b292cb748..d879f9ebd 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -36,12 +36,23 @@ NS_ASSUME_NONNULL_BEGIN - (void)notifyNewBestBlock:(DSBlock *)block; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; -- (BOOL)doAutomaticDenominating; +- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun; - (void)doMaintenance; - (void)registerCoinJoin:(CoinJoinClientOptions *)options; +- (BOOL)isRegistered; - (BOOL)isMixingFeeTx:(UInt256)txId; - (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; +- (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; +- (uint64_t)getSmallestDenomination; +- (void)updateOptions:(CoinJoinClientOptions *)options; +- (NSArray *)getStandardDenominations; +- (uint64_t)getCollateralAmount; +- (uint32_t)amountToDenomination:(uint64_t)amount; +- (int32_t)getRealOutpointCoinJoinRounds:(DSUTXO)utxo; +- (void)stopAndResetClientManager; +- (NSArray *)getSessionStatuses; + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index a7dc36a0a..5b4a25f02 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -47,12 +47,21 @@ - (void)registerCoinJoin:(CoinJoinClientOptions *)options { _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionLifecycleListener, mixingLifecycleListener, getCoinJoinKeys, destroyCoinJoinKeys); DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); - // TODO: add_wallet_ex add_client_queue_manager(_clientManager, masternodeByHash, destroyMasternodeEntry, validMNCount, AS_RUST(self)); } } } +- (BOOL)isRegistered { + return self.clientManager != NULL; +} + +- (void)updateOptions:(CoinJoinClientOptions *)options { + @synchronized (self) { + change_coinjoin_options(_clientManager, options); + } +} + - (void)setStopOnNothingToDo:(BOOL)stop { @synchronized (self) { set_stop_on_nothing_to_do(self.clientManager, stop); @@ -71,11 +80,11 @@ - (void)refreshUnusedKeys { } } -- (BOOL)doAutomaticDenominating { +- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { @synchronized (self) { - Balance *balance = [self.manager getBalance]; - BOOL result = do_automatic_denominating(_clientManager, *balance, false); - free(balance); + Balance *balance = [[self.manager getBalance] ffi_malloc]; + BOOL result = do_automatic_denominating(_clientManager, *balance, dryRun); + [DSCoinJoinBalance ffi_free:balance]; return result; } @@ -83,9 +92,9 @@ - (BOOL)doAutomaticDenominating { - (void)doMaintenance { @synchronized (self) { - Balance *balance = [self.manager getBalance]; + Balance *balance = [[self.manager getBalance] ffi_malloc]; do_maintenance(_clientManager, *balance); - free(balance); + [DSCoinJoinBalance ffi_free:balance]; } } @@ -134,7 +143,9 @@ - (void)notifyNewBestBlock:(DSBlock *)block { } - (BOOL)isMixingFeeTx:(UInt256)txId { - return is_mixing_fee_tx(_clientManager, (uint8_t (*)[32])(txId.u8)); + @synchronized (self) { + return is_mixing_fee_tx(_clientManager, (uint8_t (*)[32])(txId.u8)); + } } - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { @@ -155,6 +166,76 @@ - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transac return type; } +- (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed { + @synchronized (self) { + return get_anonymizable_balance(_clientManager, skipDenominated, skipUnconfirmed); + } +} + +- (uint64_t)getSmallestDenomination { + return coinjoin_get_smallest_denomination(); +} + +- (NSArray *)getStandardDenominations { + @synchronized (self) { + CoinJoinDenominations *denominations = get_standard_denominations(); + NSMutableArray *result = [NSMutableArray arrayWithCapacity:denominations->length]; + + for (size_t i = 0; i < denominations->length; i++) { + [result addObject:@(denominations->denoms[i])]; + } + + destroy_coinjoin_denomination(denominations); + return result; + } +} + +- (uint64_t)getCollateralAmount { + @synchronized (self) { + return get_collateral_amount(); + } +} + +- (uint32_t)amountToDenomination:(uint64_t)amount { + @synchronized (self) { + return amount_to_denomination(amount); + } +} + +- (int32_t)getRealOutpointCoinJoinRounds:(DSUTXO)utxo { + @synchronized (self) { + UInt256 hash = utxo.hash; + return get_real_outpoint_coinjoin_rounds(_clientManager, (uint8_t (*)[32])&hash, (uint32_t)utxo.n, 0); + } +} + +- (NSArray *)getSessionStatuses { + @synchronized (self) { + CoinJoinSessionStatuses* statuses = get_sessions_status(_clientManager); + + if (statuses) { + NSMutableArray *statusArray = [NSMutableArray arrayWithCapacity:statuses->length]; + + for (size_t i = 0; i < statuses->length; i++) { + PoolStatus status = statuses->statuses[i]; + [statusArray addObject:@(status)]; + } + + destroy_coinjoin_session_statuses(statuses); + + return statusArray; + } else { + return @[]; + } + } +} + +- (void)stopAndResetClientManager { + @synchronized (self) { + stop_and_reset_coinjoin(_clientManager); + } +} + - (DSChain *)chain { return self.chainManager.chain; } diff --git a/DashSync/shared/Models/CoinJoin/DSBackoff.h b/DashSync/shared/Models/CoinJoin/Utils/DSBackoff.h similarity index 100% rename from DashSync/shared/Models/CoinJoin/DSBackoff.h rename to DashSync/shared/Models/CoinJoin/Utils/DSBackoff.h diff --git a/DashSync/shared/Models/CoinJoin/DSBackoff.m b/DashSync/shared/Models/CoinJoin/Utils/DSBackoff.m similarity index 100% rename from DashSync/shared/Models/CoinJoin/DSBackoff.m rename to DashSync/shared/Models/CoinJoin/Utils/DSBackoff.m diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index f9bc6138f..9e14ca22d 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -146,6 +146,9 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // true if the address is controlled by the wallet - (BOOL)containsAddress:(NSString *)address; +// true if the coinjoin address is controlled by the wallet +- (BOOL)containsCoinJoinAddress:(NSString *)coinJoinAddress; + // true if the address is internal and is controlled by the wallet - (BOOL)containsInternalAddress:(NSString *)address; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 4ef40c0d6..88c7e72d2 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -546,6 +546,19 @@ - (BOOL)containsAddress:(NSString *)address { return FALSE; } +// true if the coinjoin address is controlled by the wallet +- (BOOL)containsCoinJoinAddress:(NSString *)coinJoinAddress { + NSParameterAssert(coinJoinAddress); + if (![coinJoinAddress isKindOfClass:[NSString class]]) { + //in case address is of type [NSNull null] + return FALSE; + } + + if ([self.coinJoinDerivationPath containsAddress:coinJoinAddress]) return TRUE; + + return FALSE; +} + // true if the address is controlled by the wallet - (BOOL)containsInternalAddress:(NSString *)address { NSParameterAssert(address); diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index c454349e4..9ae6e5f21 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -48,13 +48,14 @@ - (void)startCoinJoin { [self.coinJoinManager start]; [self.coinJoinManager refreshUnusedKeys]; + [self.coinJoinManager initMasternodeGroup]; [self.coinJoinManager setStopOnNothingToDo:true]; if (![self.coinJoinManager startMixing]) { DSLog(@"[OBJ-C] CoinJoin: Mixing has been started already."); } - [self.coinJoinManager doAutomaticDenominating]; + [self.coinJoinManager doAutomaticDenominatingWithDryRun:NO]; } @end From 3790d39ad652d086490a51c2ee5b50d0ba7dc8dc Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 6 Sep 2024 15:07:01 +0200 Subject: [PATCH 047/133] fix: coinjoin balance calculation --- .../Models/CoinJoin/DSCoinJoinManager.m | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index b8a8c1acf..9c009081b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -95,6 +95,7 @@ - (CoinJoinClientOptions *)createOptions { options->coinjoin_denoms_hardcap = DEFAULT_COINJOIN_DENOMS_HARDCAP; options->coinjoin_multi_session = NO; options->denom_only = NO; + options->chain_type = self.chain.chainType; return options; } @@ -544,37 +545,30 @@ - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { } - (Balance *)getBalance { + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; NSMutableSet *setWalletTxesCounted = [[NSMutableSet alloc] init]; uint64_t anonymizedBalance = 0; uint64_t denominatedBalance = 0; DSUTXO outpoint; - NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; + NSArray *utxos = account.unspentOutputs; for (NSValue *value in utxos) { [value getValue:&outpoint]; - - if ([setWalletTxesCounted containsObject:uint256_data(outpoint.hash)]) { - continue; + DSTransaction *tx = [account transactionForHash:outpoint.hash]; + DSTransactionOutput *output = tx.outputs[outpoint.n]; + + if ([self isCoinJoinOutput:output utxo:outpoint]) { + anonymizedBalance += output.amount; } - [setWalletTxesCounted addObject:uint256_data(outpoint.hash)]; - DSTransaction *tx = [self.chain transactionForHash:outpoint.hash]; - - for (int32_t i = 0; i < tx.outputs.count; i++) { - DSTransactionOutput *output = tx.outputs[i]; - - if ([self isCoinJoinOutput:output utxo:outpoint]) { - anonymizedBalance += output.amount; - } - - if (is_denominated_amount(output.amount)) { - denominatedBalance += output.amount; - } + if (is_denominated_amount(output.amount)) { + denominatedBalance += output.amount; + DSLog(@"[OBJ-C] CoinJoin: sum %llu (counting denominated %lu-th utxo of tx %@ amount %llu to %@)", denominatedBalance, outpoint.n, uint256_reverse_hex(tx.txHash), output.amount, output.address); } } Balance *balance = malloc(sizeof(Balance)); - balance->my_trusted = self.chain.chainManager.chain.balance; + balance->my_trusted = account.balance; balance->denominated_trusted = denominatedBalance; balance->anonymized = anonymizedBalance; balance->my_immature = 0; @@ -621,10 +615,8 @@ - (NSString *)freshAddress:(BOOL)internal { if (internal) { address = account.coinJoinChangeAddress; - DSLog(@"[OBJ-C] CoinJoin: freshChangeAddress, address: %@", address); } else { address = account.coinJoinReceiveAddress; - DSLog(@"[OBJ-C] CoinJoin: freshReceiveAddress, address: %@", address); } return address; From f3835f907a04d1295459db006b94907d291eed7e Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sat, 7 Sep 2024 16:35:48 +0200 Subject: [PATCH 048/133] fix: use DSCoinControl to only include selected inputs --- .../shared/Models/CoinJoin/DSCoinControl.h | 8 +- .../shared/Models/CoinJoin/DSCoinControl.m | 42 +++-- .../Models/CoinJoin/DSCoinJoinManager.h | 2 +- .../Models/CoinJoin/DSCoinJoinManager.m | 11 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 9 +- DashSync/shared/Models/Wallet/DSAccount.h | 3 + DashSync/shared/Models/Wallet/DSAccount.m | 146 +++++++++--------- 7 files changed, 122 insertions(+), 99 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.h b/DashSync/shared/Models/CoinJoin/DSCoinControl.h index 359b8ee17..7ab85907c 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.h @@ -16,6 +16,7 @@ // #import +#import "BigIntTypes.h" #import "dash_shared_core.h" NS_ASSUME_NONNULL_BEGIN @@ -40,11 +41,8 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFFICoinControl:(CoinControl *)coinControl; - (BOOL)hasSelected; -- (BOOL)isSelected:(NSValue *)output; -- (void)select:(NSValue *)output; -- (void)unSelect:(NSValue *)output; -- (void)unSelectAll; -- (void)useCoinJoin:(BOOL)fUseCoinJoin; +- (BOOL)isSelected:(DSUTXO)utxo; +- (void)useCoinJoin:(BOOL)useCoinJoin; - (BOOL)isUsingCoinJoin; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.m b/DashSync/shared/Models/CoinJoin/DSCoinControl.m index ad526313d..f6e388606 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.m @@ -26,6 +26,23 @@ - (instancetype)initWithFFICoinControl:(CoinControl *)coinControl { self.maxDepth = coinControl->max_depth; self.avoidAddressReuse = coinControl->avoid_address_reuse; self.allowOtherInputs = coinControl->allow_other_inputs; + + if (coinControl->set_selected && coinControl->set_selected_size > 0) { + self.setSelected = [[NSMutableOrderedSet alloc] init]; + + for (size_t i = 0; i < coinControl->set_selected_size; i++) { + TxOutPoint *outpoint = coinControl->set_selected[i]; + + if (outpoint) { + UInt256 hash; + memcpy(hash.u8, outpoint->hash, 32); + NSValue *value = dsutxo_obj(((DSUTXO){hash, outpoint->index})); + [self.setSelected addObject:value]; + } + } + } else { + self.setSelected = [[NSMutableOrderedSet alloc] init]; + } return self; } @@ -50,20 +67,17 @@ - (BOOL)hasSelected { return self.setSelected.count > 0; } -- (BOOL)isSelected:(NSValue *)output { - return [self.setSelected containsObject:output]; -} - -- (void)select:(NSValue *)output { - [self.setSelected addObject:output]; -} - -- (void)unSelect:(NSValue *)output { - [self.setSelected removeObject:output]; -} - -- (void)unSelectAll { - [self.setSelected removeAllObjects]; +- (BOOL)isSelected:(DSUTXO)utxo { + for (NSValue *selectedValue in self.setSelected) { + DSUTXO selectedUTXO; + [selectedValue getValue:&selectedUTXO]; + + if (uint256_eq(utxo.hash, selectedUTXO.hash) && utxo.n == selectedUTXO.n) { + return YES; + } + } + + return NO; } - (void)useCoinJoin:(BOOL)useCoinJoin { diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 38af0d915..e907ff885 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -59,7 +59,7 @@ NS_ASSUME_NONNULL_BEGIN - (NSString *)freshAddress:(BOOL)internal; - (NSArray *)getIssuedReceiveAddresses; - (NSArray *)getUsedReceiveAddresses; -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished; +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs coinControl:(DSCoinControl *)coinControl onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished; - (DSSimplifiedMasternodeEntry *)masternodeEntryByHash:(UInt256)hash; - (uint64_t)validMNCount; - (DSMasternodeList *)mnList; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 9c009081b..f5aaa69a2 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -488,9 +488,9 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { continue; } - NSValue *outputValue = dsutxo_obj(((DSUTXO){wtxid, i})); + DSUTXO utxo = ((DSUTXO){wtxid, i}); - if (coinControl != nil && coinControl.hasSelected && !coinControl.allowOtherInputs && ![coinControl isSelected:outputValue]) { + if (coinControl != nil && coinControl.hasSelected && !coinControl.allowOtherInputs && ![coinControl isSelected:utxo]) { continue; } @@ -498,7 +498,7 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { continue; } - if ([account isSpent:outputValue]) { + if ([account isSpent:dsutxo_obj(utxo)]) { continue; } @@ -563,7 +563,6 @@ - (Balance *)getBalance { if (is_denominated_amount(output.amount)) { denominatedBalance += output.amount; - DSLog(@"[OBJ-C] CoinJoin: sum %llu (counting denominated %lu-th utxo of tx %@ amount %llu to %@)", denominatedBalance, outpoint.n, uint256_reverse_hex(tx.txHash), output.amount, output.address); } } @@ -632,9 +631,9 @@ - (NSArray *)getUsedReceiveAddresses { return account.usedCoinJoinReceiveAddresses; } -- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished { +- (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)outputs coinControl:(DSCoinControl *)coinControl onPublished:(void (^)(UInt256 txId, NSError * _Nullable error))onPublished { DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; - DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES]; + DSTransaction *transaction = [account transactionForAmounts:amounts toOutputScripts:outputs withFee:YES coinControl:coinControl]; if (!transaction) { return NO; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index a7dc36a0a..2dcb4d47a 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -233,13 +233,13 @@ bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { return result; } -GatheredOutputs* availableCoins(bool onlySafe, CoinControl coinControl, WalletEx *walletEx, const void *context) { +GatheredOutputs* availableCoins(bool onlySafe, CoinControl *coinControl, WalletEx *walletEx, const void *context) { GatheredOutputs *gatheredOutputs; @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); ChainType chainType = wrapper.chain.chainType; - DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:&coinControl]; + DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; NSArray *coins = [wrapper.manager availableCoins:walletEx onlySafe:onlySafe coinControl:cc minimumAmount:1 maximumAmount:MAX_MONEY minimumSumAmount:MAX_MONEY maximumCount:0]; gatheredOutputs = malloc(sizeof(GatheredOutputs)); @@ -352,7 +352,7 @@ ByteArray freshCoinJoinAddress(bool internal, const void *context) { } } -bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_denominating, uint8_t (*client_session_id)[32], const void *context) { +bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinControl *coinControl, bool is_denominating, uint8_t (*client_session_id)[32], const void *context) { DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); NSMutableArray *amounts = [NSMutableArray array]; @@ -369,7 +369,8 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, bool is_d @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); - result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts onPublished:^(UInt256 txId, NSError * _Nullable error) { + DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; + result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts coinControl:cc onPublished:^(UInt256 txId, NSError * _Nullable error) { if (error) { DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); } else if (is_denominating) { diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index f9bc6138f..1d11407a8 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -26,6 +26,7 @@ #import "DSFundsDerivationPath.h" #import "DSIncomingFundsDerivationPath.h" #import "DSTransaction.h" +#import "DSCoinControl.h" #import "NSData+Dash.h" #import #import @@ -177,6 +178,8 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee; +- (DSTransaction *)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee coinControl:(DSCoinControl *)coinControl; + // 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; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 4ef40c0d6..7e14b2edc 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -61,6 +61,7 @@ #import "DSTransactionFactory.h" #import "DSTransactionInput.h" #import "DSTransactionOutput.h" +#import "DSCoinControl.h" #import "NSData+Dash.h" #import "NSDate+Utils.h" #import "NSError+Dash.h" @@ -182,7 +183,7 @@ - (void)verifyAndAssignAddedDerivationPaths:(NSArray *)deriv - (instancetype)initWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *)context { NSParameterAssert(derivationPaths); - + if (!(self = [super init])) return nil; _accountNumber = accountNumber; [self verifyAndAssignAddedDerivationPaths:derivationPaths]; @@ -206,12 +207,12 @@ - (instancetype)initWithAccountNumber:(uint32_t)accountNumber withDerivationPath - (instancetype)initAsViewOnlyWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *)context { NSParameterAssert(derivationPaths); - + if (!(self = [self initWithAccountNumber:accountNumber withDerivationPaths:derivationPaths inContext:context])) return nil; self.isViewOnlyAccount = TRUE; self.transactionsToSave = [NSMutableArray array]; self.transactionsToSaveInBlockSave = [NSMutableDictionary dictionary]; - + return self; } @@ -236,17 +237,17 @@ - (void)loadTransactions { DSLogPrivate(@"[%@] Transaction %@", _wallet.chain.name, [transaction longDescription]); } #endif - + NSUInteger transactionCount = [DSTransactionEntity countObjectsInContext:self.managedObjectContext matching:@"transactionHash.chain == %@", [self.wallet.chain chainEntityInContext:self.managedObjectContext]]; if (transactionCount > self.allTx.count) { // pre-fetch transaction inputs and outputs @autoreleasepool { NSFetchRequest *fetchRequest = [DSTxOutputEntity fetchRequest]; - + //for some reason it is faster to search by the wallet unique id on the account, then it is by the account itself, this might change if there are more than 1 account; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"account.walletUniqueID = %@ && account.index = %@", self.wallet.uniqueIDString, @(self.accountNumber)]; [fetchRequest setRelationshipKeyPathsForPrefetching:@[@"transaction.inputs", @"transaction.outputs", @"transaction.transactionHash", @"spentInInput.transaction.inputs", @"spentInInput.transaction.outputs", @"spentInInput.transaction.transactionHash"]]; - + NSError *fetchRequestError = nil; //NSDate *transactionOutputsStartTime = [NSDate date]; NSArray *transactionOutputs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchRequestError]; @@ -331,7 +332,7 @@ - (NSString *)uniqueID { - (uint32_t)blockHeight { static uint32_t height = 0; uint32_t h = self.wallet.chain.lastSyncBlockHeight; - + if (h > height) height = h; return height; } @@ -377,7 +378,7 @@ - (NSArray *)unspentOutputs { - (void)removeDerivationPath:(DSDerivationPath *)derivationPath { NSParameterAssert(derivationPath); - + if ([self.mFundDerivationPaths containsObject:derivationPath]) { [self.mFundDerivationPaths removeObject:derivationPath]; } @@ -401,7 +402,7 @@ - (DSIncomingFundsDerivationPath *)derivationPathForFriendshipWithIdentifier:(NS - (void)addDerivationPath:(DSDerivationPath *)derivationPath { NSParameterAssert(derivationPath); - + if (!_isViewOnlyAccount) { [self verifyAndAssignAddedDerivationPaths:@[derivationPath]]; } @@ -436,7 +437,7 @@ - (void)addOutgoingDerivationPath:(DSIncomingFundsDerivationPath *)derivationPat - (void)addDerivationPathsFromArray:(NSArray *)derivationPaths { NSParameterAssert(derivationPaths); - + if (!_isViewOnlyAccount) { [self verifyAndAssignAddedDerivationPaths:derivationPaths]; } @@ -539,7 +540,7 @@ - (BOOL)containsAddress:(NSString *)address { //in case address is of type [NSNull null] return FALSE; } - + for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath containsAddress:address]) return TRUE; } @@ -553,7 +554,7 @@ - (BOOL)containsInternalAddress:(NSString *)address { //in case address is of type [NSNull null] return FALSE; } - + for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]] && [derivationPath containsChangeAddress:address]) { return TRUE; @@ -568,7 +569,7 @@ - (BOOL)baseDerivationPathsContainAddress:(NSString *)address { //in case address is of type [NSNull null] return FALSE; } - + for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]] && [derivationPath containsAddress:address]) { return TRUE; @@ -584,7 +585,7 @@ - (BOOL)containsExternalAddress:(NSString *)address { //in case address is of type [NSNull null] return FALSE; } - + for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { if ([(DSFundsDerivationPath *)derivationPath containsReceiveAddress:address]) return TRUE; @@ -601,7 +602,7 @@ - (DSIncomingFundsDerivationPath *)externalDerivationPathContainingAddress:(NSSt //in case address is of type [NSNull null] return nil; } - + for (DSIncomingFundsDerivationPath *derivationPath in self.mContactOutgoingFundDerivationPathsDictionary.allValues) { if ([derivationPath containsAddress:address]) return derivationPath; } @@ -615,7 +616,7 @@ - (BOOL)addressIsUsed:(NSString *)address { //in case address is of type [NSNull null] return FALSE; } - + for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath addressIsUsed:address]) return TRUE; } @@ -628,11 +629,11 @@ - (BOOL)transactionAddressAlreadySeenInOutputs:(NSString *)address { //in case address is of type [NSNull null] return FALSE; } - + for (DSTransaction *transaction in self.allTransactions) { if ([transaction.outputs indexOfObjectPassingTest:^BOOL(DSTransactionOutput *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - return [obj.address isEqual:address]; - }] != NSNotFound) return TRUE; + return [obj.address isEqual:address]; + }] != NSNotFound) return TRUE; } return FALSE; } @@ -646,11 +647,11 @@ - (void)updateBalance { NSMutableDictionary *pendingCoinbaseLockedTransactionHashes = [NSMutableDictionary dictionary]; NSMutableArray *balanceHistory = [NSMutableArray array]; uint32_t now = [NSDate timeIntervalSince1970]; - + for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { derivationPath.balance = 0; } - + for (DSTransaction *tx in [self.transactions reverseObjectEnumerator]) { #if LOG_BALANCE_UPDATE DSLogPrivate(@"updating balance after transaction %@", [NSData dataWithUInt256:tx.txHash].reverse.hexString); @@ -660,7 +661,7 @@ - (void)updateBalance { NSSet *inputs; uint32_t n = 0; BOOL pending = NO; - + if (!tx.isCoinbaseClassicTransaction && ![tx isKindOfClass:[DSCoinbaseTransaction class]]) { NSMutableArray *rHashes = [NSMutableArray array]; @@ -680,19 +681,19 @@ - (void)updateBalance { } else { inputs = [NSSet set]; } - + [spentOutputs unionSet:spent]; // add inputs to spent output set n = 0; - + // check if any inputs are pending if (tx.blockHeight == TX_UNCONFIRMED) { if (tx.size > TX_MAX_SIZE) { pending = YES; // check transaction size is under TX_MAX_SIZE } - + for (DSTransactionInput *input in tx.inputs) { if (input.sequence == UINT32_MAX) continue; - + if (tx.lockTime < TX_MAX_LOCK_HEIGHT && tx.lockTime > self.wallet.chain.bestBlockHeight + 1) { pending = YES; // future lockTime @@ -712,7 +713,7 @@ - (void)updateBalance { #endif } } - + for (DSTransactionOutput *output in tx.outputs) { // check that no outputs are dust if (output.amount < TX_MIN_OUTPUT_AMOUNT) { pending = YES; @@ -724,15 +725,15 @@ - (void)updateBalance { } } } - + if (pending || [inputs intersectsSet:pendingTransactionHashes]) { [pendingTransactionHashes addObject:uint256_obj(tx.txHash)]; [balanceHistory insertObject:@(balance) atIndex:0]; continue; } - + uint32_t lockedBlockHeight = [self transactionOutputsAreLockedTill:tx]; - + if (lockedBlockHeight) { if (![pendingCoinbaseLockedTransactionHashes objectForKey:@(lockedBlockHeight)]) { pendingCoinbaseLockedTransactionHashes[@(lockedBlockHeight)] = [NSMutableSet set]; @@ -741,7 +742,7 @@ - (void)updateBalance { [balanceHistory insertObject:@(balance) atIndex:0]; continue; } - + //TODO: don't add outputs below TX_MIN_OUTPUT_AMOUNT //TODO: don't add coin generation outputs < 100 blocks deep //NOTE: balance/UTXOs will then need to be recalculated when last block changes @@ -759,15 +760,15 @@ - (void)updateBalance { } n++; } - + // transaction ordering is not guaranteed, so check the entire UTXO set against the entire spent output set [spent setSet:utxos.set]; [spent intersectSet:spentOutputs]; - + for (NSValue *output in spent) { // remove any spent outputs from UTXO set DSTransaction *transaction; DSUTXO o; - + [output getValue:&o]; transaction = self.allTx[uint256_obj(o.hash)]; [utxos removeObject:output]; @@ -781,7 +782,7 @@ - (void)updateBalance { } } } - + if (prevBalance < balance) totalReceived += balance - prevBalance; if (balance < prevBalance) totalSent += prevBalance - balance; [balanceHistory insertObject:@(balance) atIndex:0]; @@ -802,7 +803,7 @@ - (void)updateBalance { #endif } } - + self.invalidTransactionHashes = invalidTx; self.pendingTransactionHashes = pendingTransactionHashes; self.pendingCoinbaseLockedTransactionHashes = pendingCoinbaseLockedTransactionHashes; @@ -811,10 +812,10 @@ - (void)updateBalance { self.balanceHistory = balanceHistory; _totalSent = totalSent; _totalReceived = totalReceived; - + if (balance != _balance) { _balance = balance; - + dispatch_async(dispatch_get_main_queue(), ^{ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(postBalanceDidChangeNotification) object:nil]; [self performSelector:@selector(postBalanceDidChangeNotification) withObject:nil afterDelay:0.1]; @@ -825,9 +826,9 @@ - (void)updateBalance { // historical wallet balance after the given transaction, or current balance if transaction is not registered in wallet - (uint64_t)balanceAfterTransaction:(DSTransaction *)transaction { NSParameterAssert(transaction); - + NSUInteger i = [self.transactions indexOfObject:transaction]; - + return (i < self.balanceHistory.count) ? [self.balanceHistory[i] unsignedLongLongValue] : self.balance; } @@ -846,7 +847,7 @@ static NSUInteger transactionAddressIndex(DSTransaction *transaction, NSArray *a NSUInteger i = [addressChain indexOfObject:output.address]; if (i != NSNotFound) return i; } - + return NSNotFound; } @@ -861,11 +862,11 @@ __block __weak BOOL (^_isAscending)(id, id) = isAscending = ^BOOL(DSTransaction if (tx1.blockHeight < tx2.blockHeight) return NO; NSValue *hash1 = uint256_obj(tx1.txHash), *hash2 = uint256_obj(tx2.txHash); if ([tx1.inputs indexOfObjectPassingTest:^BOOL(DSTransactionInput *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - return uint256_eq(obj.inputHash, tx2.txHash); - }] != NSNotFound) return YES; + return uint256_eq(obj.inputHash, tx2.txHash); + }] != NSNotFound) return YES; if ([tx2.inputs indexOfObjectPassingTest:^BOOL(DSTransactionInput *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - return uint256_eq(obj.inputHash, tx1.txHash); - }] != NSNotFound) return NO; + return uint256_eq(obj.inputHash, tx1.txHash); + }] != NSNotFound) return NO; if ([self.invalidTransactionHashes containsObject:hash1] && ![self.invalidTransactionHashes containsObject:hash2]) return YES; if ([self.pendingTransactionHashes containsObject:hash1] && ![self.pendingTransactionHashes containsObject:hash2]) return YES; for (DSTransactionInput *input in tx1.inputs) { @@ -873,19 +874,19 @@ __block __weak BOOL (^_isAscending)(id, id) = isAscending = ^BOOL(DSTransaction } return NO; }; - + [self.transactions sortWithOptions:NSSortStable usingComparator:^NSComparisonResult(id tx1, id tx2) { - if (isAscending(tx1, tx2)) return NSOrderedAscending; - if (isAscending(tx2, tx1)) return NSOrderedDescending; - - NSUInteger i = transactionAddressIndex(tx1, self.internalAddresses); - NSUInteger j = transactionAddressIndex(tx2, (i == NSNotFound) ? self.externalAddresses : self.internalAddresses); - - if (i == NSNotFound && j != NSNotFound) i = transactionAddressIndex(tx1, self.externalAddresses); - if (i == NSNotFound || j == NSNotFound || i == j) return NSOrderedSame; - return (i > j) ? NSOrderedAscending : NSOrderedDescending; - }]; + if (isAscending(tx1, tx2)) return NSOrderedAscending; + if (isAscending(tx2, tx1)) return NSOrderedDescending; + + NSUInteger i = transactionAddressIndex(tx1, self.internalAddresses); + NSUInteger j = transactionAddressIndex(tx2, (i == NSNotFound) ? self.externalAddresses : self.internalAddresses); + + if (i == NSNotFound && j != NSNotFound) i = transactionAddressIndex(tx1, self.externalAddresses); + if (i == NSNotFound || j == NSNotFound || i == j) return NSOrderedSame; + return (i > j) ? NSOrderedAscending : NSOrderedDescending; + }]; } } @@ -903,7 +904,7 @@ - (DSTransaction *)transactionForHash:(UInt256)txHash { // last 100 transactions sorted by date, most recent first - (NSArray *)recentTransactions { return [self.transactions.array subarrayWithRange:NSMakeRange(0, (self.transactions.count > 100) ? 100 : - self.transactions.count)]; + self.transactions.count)]; } // last 100 transactions sorted by date, most recent first @@ -971,14 +972,14 @@ - (BOOL)canContainTransaction:(DSTransaction *)transaction { DSProviderUpdateRegistrarTransaction *providerUpdateRegistrarTransaction = (DSProviderUpdateRegistrarTransaction *)transaction; if ([self containsAddress:providerUpdateRegistrarTransaction.payoutAddress]) return YES; } - + return NO; } } - (BOOL)checkIsFirstTransaction:(DSTransaction *)transaction { NSParameterAssert(transaction); - + for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath type] & DSDerivationPathType_IsForFunds) { NSString *firstAddress; @@ -988,8 +989,8 @@ - (BOOL)checkIsFirstTransaction:(DSTransaction *)transaction { firstAddress = [(DSIncomingFundsDerivationPath *)derivationPath addressAtIndex:0]; } if ([transaction.outputs indexOfObjectPassingTest:^BOOL(DSTransactionOutput *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { - return [obj.address isEqual:firstAddress]; - }] != NSNotFound) { + return [obj.address isEqual:firstAddress]; + }] != NSNotFound) { return TRUE; } } @@ -1009,11 +1010,11 @@ - (DSTransaction *)transactionFor:(uint64_t)amount to:(NSString *)address withFe // 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 { 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]; } @@ -1021,14 +1022,18 @@ - (DSCreditFundingTransaction *)creditFundingTransactionFor:(uint64_t)amount to: // 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]; + return [self transactionForAmounts:amounts toOutputScripts:scripts withFee:fee toShapeshiftAddress:nil coinControl:nil]; } -- (DSTransaction *)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee toShapeshiftAddress:(NSString *)shapeshiftAddress { +- (DSTransaction *)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee coinControl:(DSCoinControl *)coinControl { + return [self transactionForAmounts:amounts toOutputScripts:scripts withFee:fee toShapeshiftAddress:nil coinControl:coinControl]; +} + +- (DSTransaction *)transactionForAmounts:(NSArray *)amounts toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee toShapeshiftAddress:(NSString *)shapeshiftAddress coinControl:(DSCoinControl *)coinControl { 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 coinControl:coinControl]; } // MARK: == Proposal Transaction Creation @@ -1055,7 +1060,7 @@ - (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 coinControl:nil]; } // returns an unsigned transaction that sends the specified amounts from the wallet to the specified output scripts @@ -1064,7 +1069,8 @@ - (DSTransaction *)updateTransaction:(DSTransaction *)transaction toOutputScripts:(NSArray *)scripts withFee:(BOOL)fee toShapeshiftAddress:(NSString *)shapeshiftAddress - sortType:(DSTransactionSortType)sortType { + sortType:(DSTransactionSortType)sortType + coinControl:(DSCoinControl *)coinControl { NSParameterAssert(transaction); NSParameterAssert(amounts); NSParameterAssert(scripts); @@ -1085,6 +1091,8 @@ - (DSTransaction *)updateTransaction:(DSTransaction *)transaction // attacker double spending and requesting a refund for (NSValue *output in self.utxos) { [output getValue:&o]; + + if (coinControl && ![coinControl isSelected:o]) continue; tx = self.allTx[uint256_obj(o.hash)]; if ([self transactionOutputsAreLocked:tx]) continue; if (!tx) continue; From 41f025d4976048d7eb641742e4c107c766e1fa0f Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 15 Sep 2024 11:27:06 +0200 Subject: [PATCH 049/133] fix: wallet integration fixes --- .../Models/CoinJoin/DSCoinJoinManager.m | 26 ++++++++++++++----- .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 ++ .../Models/CoinJoin/DSCoinJoinWrapper.m | 12 +++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index cdf9d30f9..501a4b4ef 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -271,6 +271,16 @@ - (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { return false; } +// NSArray *issued = [self getUsedReceiveAddresses]; +// DSLog(@"[OBJ-C] CoinJoin: keys used: %lu", (unsigned long)issued.count); +// +// NSArray *all = [self getIssuedReceiveAddresses]; +// DSLog(@"[OBJ-C] CoinJoin: all keys count: %lu", (unsigned long)all.count); +// +// for (NSString *iss in issued) { +// DSLog(@"[OBJ-C] CoinJoin: %@", iss); +// } + if (![self.wrapper isRegistered]) { [self.wrapper registerCoinJoin:_options]; } @@ -608,12 +618,12 @@ - (double)getMixingProgress { DSTransactionOutput *output = tx.outputs[outpoint.n]; __block int unmixedInputs = 0; - __block uint64_t outputValue = output.amount - collateralAmount; + __block int64_t outputValue = output.amount - collateralAmount; [denominations enumerateObjectsUsingBlock:^(NSNumber *coin, NSUInteger idx, BOOL *stop) { - while (outputValue > coin.unsignedLongLongValue) { + while (outputValue - coin.longLongValue > 0) { unmixedInputs++; - outputValue -= coin.unsignedLongLongValue; + outputValue -= coin.longLongValue; } }]; @@ -661,11 +671,11 @@ - (double)getMixingProgress { } - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { - if (!is_denominated_amount(output.amount)) { + if (![self.wrapper isDenominatedAmount:output.amount]) { return false; } - if (!is_fully_mixed_with_manager(_wrapper.clientManager, (uint8_t (*)[32])(utxo.hash.u8), (uint32_t)utxo.n)) { + if (![self.wrapper isFullyMixed:utxo]) { return false; } @@ -673,6 +683,10 @@ - (BOOL)isCoinJoinOutput:(DSTransactionOutput *)output utxo:(DSUTXO)utxo { } - (DSCoinJoinBalance *)getBalance { + if (![self.wrapper isRegistered]) { + [self.wrapper registerCoinJoin:self.options]; + } + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; uint64_t anonymizedBalance = 0; uint64_t denominatedBalance = 0; @@ -688,7 +702,7 @@ - (DSCoinJoinBalance *)getBalance { anonymizedBalance += output.amount; } - if (is_denominated_amount(output.amount)) { + if ([self.wrapper isDenominatedAmount:output.amount]) { denominatedBalance += output.amount; } } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index d879f9ebd..b8c1de0a8 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -42,6 +42,8 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isRegistered; - (BOOL)isMixingFeeTx:(UInt256)txId; - (void)refreshUnusedKeys; +- (BOOL)isDenominatedAmount:(uint64_t)amount; +- (BOOL)isFullyMixed:(DSUTXO)utxo; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; - (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; - (uint64_t)getSmallestDenomination; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 2039a7f1a..7ce2ec7c7 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -98,6 +98,18 @@ - (void)doMaintenance { } } +- (BOOL)isDenominatedAmount:(uint64_t)amount { + @synchronized (self) { + return is_denominated_amount(amount); + } +} + +- (BOOL)isFullyMixed:(DSUTXO)utxo { + @synchronized (self) { + return is_fully_mixed_with_manager(_clientManager, (uint8_t (*)[32])(utxo.hash.u8), (uint32_t)utxo.n); + } +} + - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { @synchronized (self) { DSLog(@"[OBJ-C] CoinJoin: process DSQ from %@", peer.location); From 49b001b3a003a443fd3fc6d2cd1a470e2cb7638a Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sat, 21 Sep 2024 20:23:10 +0700 Subject: [PATCH 050/133] fix: simplify MasternodeGroup --- .../shared/Models/CoinJoin/DSCoinControl.m | 2 +- .../Models/CoinJoin/DSCoinJoinManager.m | 23 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 4 - .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 231 ++++++------------ DashSync/shared/Models/Wallet/DSAccount.m | 4 + 5 files changed, 81 insertions(+), 183 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.m b/DashSync/shared/Models/CoinJoin/DSCoinControl.m index f6e388606..cf7411e90 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.m @@ -72,7 +72,7 @@ - (BOOL)isSelected:(DSUTXO)utxo { DSUTXO selectedUTXO; [selectedValue getValue:&selectedUTXO]; - if (uint256_eq(utxo.hash, selectedUTXO.hash) && utxo.n == selectedUTXO.n) { + if (dsutxo_eq(utxo, selectedUTXO)) { return YES; } } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 501a4b4ef..f6d986682 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -92,7 +92,7 @@ - (CoinJoinClientOptions *)createOptions { CoinJoinClientOptions *options = malloc(sizeof(CoinJoinClientOptions)); options->enable_coinjoin = YES; options->coinjoin_rounds = 1; - options->coinjoin_sessions = 1; + options->coinjoin_sessions = 6; options->coinjoin_amount = DUFFS / 8; options->coinjoin_random_rounds = COINJOIN_RANDOM_ROUNDS; options->coinjoin_denoms_goal = DEFAULT_COINJOIN_DENOMS_GOAL; @@ -271,18 +271,8 @@ - (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { return false; } -// NSArray *issued = [self getUsedReceiveAddresses]; -// DSLog(@"[OBJ-C] CoinJoin: keys used: %lu", (unsigned long)issued.count); -// -// NSArray *all = [self getIssuedReceiveAddresses]; -// DSLog(@"[OBJ-C] CoinJoin: all keys count: %lu", (unsigned long)all.count); -// -// for (NSString *iss in issued) { -// DSLog(@"[OBJ-C] CoinJoin: %@", iss); -// } - if (![self.wrapper isRegistered]) { - [self.wrapper registerCoinJoin:_options]; + [self.wrapper registerCoinJoin:self.options]; } DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); @@ -385,7 +375,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { continue; } - if (is_locked_coin(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { + if ([account isSpent:dsutxo_obj(((DSUTXO){outpoint.hash, i}))] || is_locked_coin(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { continue; } @@ -437,7 +427,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { } // Note: cache is assigned in dash-shared-core - + return vecTallyRet; } } @@ -824,19 +814,16 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { } - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port warn:(BOOL)warn { - DSLog(@"[OBJ-C] CoinJoin peers: sendMessageOfType: %@ to %@", messageType, [self.masternodeGroup hostFor:address]); - return [self.masternodeGroup forPeer:address port:port warn:YES withPredicate:^BOOL(DSPeer * _Nonnull peer) { + return [self.masternodeGroup forPeer:address port:port warn:warn withPredicate:^BOOL(DSPeer * _Nonnull peer) { if ([messageType isEqualToString:DSCoinJoinAcceptMessage.type]) { DSCoinJoinAcceptMessage *request = [DSCoinJoinAcceptMessage requestWithData:message]; [peer sendRequest:request]; } else if ([messageType isEqualToString:DSCoinJoinEntryMessage.type]) { DSCoinJoinEntryMessage *request = [DSCoinJoinEntryMessage requestWithData:message]; [peer sendRequest:request]; - DSLog(@"[OBJ-C] CoinJoin dsi: sent"); } else if ([messageType isEqualToString:DSCoinJoinSignedInputs.type]) { DSCoinJoinSignedInputs *request = [DSCoinJoinSignedInputs requestWithData:message]; [peer sendRequest:request]; - DSLog(@"[OBJ-C] CoinJoin dss: sent"); } else { DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); return NO; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 7ce2ec7c7..42aa373ba 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -112,8 +112,6 @@ - (BOOL)isFullyMixed:(DSUTXO)utxo { - (void)processDSQueueFrom:(DSPeer *)peer message:(NSData *)message { @synchronized (self) { - DSLog(@"[OBJ-C] CoinJoin: process DSQ from %@", peer.location); - ByteArray *array = malloc(sizeof(ByteArray)); array->len = (uintptr_t)message.length; array->ptr = data_malloc(message); @@ -414,8 +412,6 @@ void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { } Transaction* signTransaction(Transaction *transaction, bool anyoneCanPay, const void *context) { - DSLog(@"[OBJ-C CALLBACK] CoinJoin: signTransaction"); - @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index ed44dc49a..eea324e0e 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -1,4 +1,4 @@ -// +// // Created by Andrei Ashikhmin // Copyright © 2024 Dash Core Group. All rights reserved. // @@ -50,7 +50,6 @@ @interface DSMasternodeGroup () @property (nullable, nonatomic, readwrite) DSPeer *downloadPeer; @property (nonatomic, strong) DSBackoff *groupBackoff; @property (nonatomic, strong) NSMutableDictionary *backoffMap; -@property (nonatomic, strong) NSMutableArray *inactives; @property (nonatomic, strong) NSLock *lock; @property (nonatomic) uint32_t lastSeenBlock; @@ -75,7 +74,6 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _shouldSendDsq = true; _groupBackoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:GROUP_BACKOFF_MULTIPLIER]; _backoffMap = [NSMutableDictionary dictionary]; - _inactives = [NSMutableArray array]; _lastSeenBlock = 0; } return self; @@ -117,7 +115,7 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { - DSLog(@"[OBJ-C] CoinJoin peers: masternode[closing] %@", [self hostFor:ip]); + DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); @synchronized (self.pendingClosingMasternodes) { [self.pendingClosingMasternodes addObject:peer]; @@ -146,10 +144,10 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B if (warn) { if (![self isNodePending:ip port:port]) { - DSLog(@"[OBJ-C] CoinJoin peers: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); -// NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); TODO + DSLog(@"[OBJ-C] CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); + NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); } else { - DSLog(@"[OBJ-C] CoinJoin peers: %@ in the list of pending peers: %@", [self hostFor:ip], listOfPeers); + DSLog(@"[OBJ-C] CoinJoin: %@ in the list of pending peers", [self hostFor:ip]); } } @@ -193,76 +191,32 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { return; } - BOOL doDiscovery = NO; - NSDate *now = [NSDate date]; - - @synchronized (self.inactives) { - BOOL havPeersToTry = self.inactives.count > 0 && [self.backoffMap objectForKey:self.inactives[0].location].retryTime <= now; + @synchronized (self.groupBackoff) { NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; - doDiscovery = !havPeersToTry || numPeers <= 0; - DSPeer *peerToTry = nil; - NSDate *retryTime = nil; - - if (doDiscovery) { - NSArray *peers = [self getPeers]; - - for (DSPeer *peer in peers) { - [self addInactive:peer]; - } - - BOOL discoverySuccess = peers.count > 0; - // Require that we have enough connections, to consider this - // a success, or we just constantly test for new peers - if (discoverySuccess && numPeers >= self.maxConnections) { - [self.groupBackoff trackSuccess]; - } else { - [self.groupBackoff trackFailure]; - } - } - - // Inactives is sorted by backoffMap time. - if (self.inactives.count == 0) { - if (numPeers < self.maxConnections) { - NSTimeInterval interval = MAX([self.groupBackoff.retryTime timeIntervalSinceDate:now], MIN_PEER_DISCOVERY_INTERVAL); - - DSLog(@"[OBJ-C] CoinJoin: Masternode discovery didn't provide us any more masternodes, will try again in %fl ms. MaxConnections: %lu", interval, (unsigned long)self.maxConnections); - - [self triggerConnectionsJobWithDelay:interval]; - } else { - // We have enough peers and discovery provided no more, so just settle down. Most likely we - // were given a fixed set of addresses in some test scenario. - } + + if (numPeers >= self.maxConnections) { return; - } else { - NSMutableString *backoffs = [NSMutableString string]; - - for (DSPeer *peer in self.inactives) { - DSBackoff *backoff = [self.backoffMap objectForKey:peer.location]; - [backoffs appendFormat:@"[%@ : %@], ", peer.location, backoff.retryTime]; - } - - DSLog(@"[OBJ-C] CoinJoin: backoffs for inactives before peerToTry: %@", backoffs); - - peerToTry = self.inactives.firstObject; - [self.inactives removeObjectAtIndex:0]; - retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; } - - if (numPeers > 0) { + + NSDate *now = [NSDate date]; + DSPeer *peerToTry = [self getNextPendingMasternode]; + + if (peerToTry) { + NSDate *retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; - } - - NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; + NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; - if (delay > 0) { - DSLog(@"[OBJ-C] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); - [self.inactives addObject:peerToTry]; - [self sortInactives]; - [self triggerConnectionsJobWithDelay:delay]; - return; + if (delay > 0.1) { + DSLog(@"[OBJ-C] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); + [self triggerConnectionsJobWithDelay:delay]; + return; + } + + [self connectTo:peerToTry]; + [self.groupBackoff trackSuccess]; + } else { + [self.groupBackoff trackFailure]; } - - [self connectTo:peerToTry]; } NSUInteger count = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; @@ -273,14 +227,15 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { }); } -- (NSArray *)getPeers { - NSMutableArray *addresses = [NSMutableArray array]; - - @synchronized(self.addressMap) { - NSArray *pendingSessionsCopy = [self.pendingSessions copy]; - +- (DSPeer *)getNextPendingMasternode { + @synchronized(self.addressMap) { + NSArray *pendingSessionsCopy = [self.pendingSessions copy]; + DSPeer *peerWithLeastBackoff = nil; + NSValue *sessionValueWithLeastBackoff = nil; + UInt256 sessionId = UINT256_ZERO; + NSDate *leastBackoffTime = [NSDate distantFuture]; + for (NSValue *sessionValue in pendingSessionsCopy) { - UInt256 sessionId; [sessionValue getValue:&sessionId]; DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; @@ -290,22 +245,34 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { DSPeer *peer = [self peerForLocation:ipAddress port:port]; if (peer == nil) { - DSLog(@"[OBJ-C] CoinJoin peers: not found for %@, creating new", [self hostFor:ipAddress]); + DSLog(@"[OBJ-C] CoinJoin: not found for %@, creating new", [self hostFor:ipAddress]); peer = [DSPeer peerWithAddress:ipAddress andPort:port onChain:self.chain]; } - if (![self.pendingClosingMasternodes containsObject:peer]) { - [addresses addObject:peer]; - [self.addressMap setObject:sessionValue forKey:peer.location]; - DSLog(@"[OBJ-C] CoinJoin peers: discovery: %@ -> %@", peer.location, uint256_hex(sessionId)); + if (![self.pendingClosingMasternodes containsObject:peer] && ![self isNodeConnected:peer] && ![self isNodePending:peer]) { + DSBackoff *backoff = [self.backoffMap objectForKey:peer.location]; + + if (!backoff) { + backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:BACKOFF_MULTIPLIER]; + [self.backoffMap setObject:backoff forKey:peer.location]; + } + + if ([backoff.retryTime compare:leastBackoffTime] == NSOrderedAscending) { + leastBackoffTime = backoff.retryTime; + peerWithLeastBackoff = peer; + sessionValueWithLeastBackoff = sessionValue; + } } - } else { - DSLog(@"[OBJ-C] CoinJoin peers: mixingMasternodeInfo is nil"); } - } + } + + if (peerWithLeastBackoff) { + [self.addressMap setObject:sessionValueWithLeastBackoff forKey:peerWithLeastBackoff.location]; + DSLog(@"[OBJ-C] CoinJoin: discovery: %@ -> %@", peerWithLeastBackoff.location, uint256_hex(sessionId)); + } + + return peerWithLeastBackoff; } - - return [addresses copy]; } - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { @@ -324,7 +291,7 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self.pendingSessions) { - DSLog(@"[OBJ-C] CoinJoin peers: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); + DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [self.pendingSessions addObject:sessionIdValue]; @@ -332,18 +299,17 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session [self.masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; [self.sessionMap setObject:proTxHashKey forKey:sessionIdValue]; - [self updateMaxConnections]; [self checkMasternodesWithoutSessions]; + [self updateMaxConnections]; } return true; } - (void)updateMaxConnections { - DSLog(@"[OBJ-C] CoinJoin peers: updateMaxConnections, pendingSessions.count: %lu", self.pendingSessions.count); _maxConnections = self.pendingSessions.count; - NSUInteger connections = MIN(_maxConnections, DEFAULT_COINJOIN_SESSIONS); - DSLog(@"[OBJ-C] CoinJoin peers: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)DEFAULT_COINJOIN_SESSIONS); + NSUInteger connections = MIN(self.maxConnections, self.coinJoinManager.options->coinjoin_sessions); + DSLog(@"[OBJ-C] CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); [self updateMaxConnections:connections]; } @@ -364,25 +330,12 @@ - (void)updateMaxConnections:(NSUInteger)connections { NSUInteger connectedCount = connectedPeers.count; NSUInteger numPeers = pendingCount + connectedCount; adjustment = self.maxConnections - numPeers; - DSLog(@"[OBJ-C] CoinJoin peers: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); + DSLog(@"[OBJ-C] CoinJoin: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); } if (adjustment > 0) { - DSLog(@"[OBJ-C] CoinJoin peers: triggerConnections for adjustment"); [self triggerConnections]; } - - if (adjustment < 0) { - for (DSPeer *peer in connectedPeers) { - DSLog(@"[OBJ-C] CoinJoin peers: adjustment < 0, disconnecting peer %@", peer.location); - [peer disconnect]; - adjustment++; - - if (adjustment >= 0) { - break; - } - } - } } - (void)checkMasternodesWithoutSessions { @@ -405,21 +358,22 @@ - (void)checkMasternodesWithoutSessions { } } else { // TODO(DashJ): we may not need this anymore - DSLog(@"[OBJ-C] CoinJoin peers: session is not connected to a masternode: %@", uint256_hex(sessionId)); + DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); } } - + if (!found) { - DSLog(@"[OBJ-C] CoinJoin peers: masternode is not connected to a session: %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin: masternode is not connected to a session: %@", peer.location); [masternodesToDrop addObject:peer]; } } - DSLog(@"[OBJ-C] CoinJoin peers: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); + DSLog(@"[OBJ-C] CoinJoin: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; - DSLog(@"[OBJ-C] CoinJoin peers: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + DSLog(@"[OBJ-C] CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + [self.pendingClosingMasternodes addObject:peer]; [peer disconnect]; } } @@ -434,7 +388,7 @@ - (NSString *)hostFor:(UInt128)address { } - (BOOL)connectTo:(DSPeer *)peer { - DSLog(@"[OBJ-C] CoinJoin peers: connectTo: %@", peer.location); + DSLog(@"[OBJ-C] CoinJoin: connectTo: %@", peer.location); if (![self isMasternodeSessionByPeer:peer]) { DSLog(@"[OBJ-C] CoinJoin: %@ not a masternode session, exit", peer.location); @@ -492,7 +446,7 @@ - (BOOL)isMasternodeSessionByPeer:(DSPeer *)peer { } - (BOOL)isNodeConnected:(DSPeer *)node { - return [self forPeer:node.address port:node.port warn:false withPredicate:^BOOL(DSPeer * _Nonnull peer) { + return [self forPeer:node.address port:node.port warn:NO withPredicate:^BOOL(DSPeer * _Nonnull peer) { return YES; }]; } @@ -530,12 +484,10 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; - DSLog(@"[OBJ-C] CoinJoin peers: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); + DSLog(@"[OBJ-C] CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; - // Put back on inactive list - [self addInactive:peer]; NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; if (numPeers < self.maxConnections) { @@ -552,7 +504,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err } } - DSLog(@"[OBJ-C] CoinJoin peers: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"[OBJ-C] CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); if (masternode) { NSString *address = peer.location; @@ -585,28 +537,6 @@ - (void)peer:(nonnull DSPeer *)peer relayedPeers:(nonnull NSArray *)peers { // TODO ? } -- (void)addInactive:(DSPeer *)peer { - DSLog(@"[OBJ-C] CoinJoin peers: addInactive: %@, currentCount: %lu", peer.location, (unsigned long)self.inactives.count); - - @synchronized (self.inactives) { - // Deduplicate, handle differently than PeerGroup - if ([self.inactives containsObject:peer]) { - return; - } - - // do not connect to another the same peer twice - if ([self isNodeConnected:peer]) { - DSLog(@"[OBJ-C] CoinJoin: attempting to connect to the same masternode again: %@", peer.location); - return; - } - - DSBackoff *backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:BACKOFF_MULTIPLIER]; - [self.backoffMap setObject:backoff forKey:peer.location]; - [self.inactives insertObject:peer atIndex:0]; - [self sortInactives]; - } -} - - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { for (DSPeer *peer in self.connectedPeers) { if (uint128_eq(peer.address, ipAddress) && peer.port == port) { @@ -623,29 +553,10 @@ - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { return [self.chain.chainManager.peerManager peerForLocation:ipAddress port:port]; } -- (void)sortInactives { - [_inactives sortUsingComparator:^NSComparisonResult(DSPeer *obj1, DSPeer *obj2) { - DSBackoff *backoff1 = [_backoffMap objectForKey:obj1.location]; - DSBackoff *backoff2 = [_backoffMap objectForKey:obj2.location]; - return [backoff1.retryTime compare:backoff2.retryTime]; - }]; - - if (_inactives.count > 1) { - NSMutableString *backoffs = [NSMutableString string]; - - for (DSPeer *peer in _inactives) { - DSBackoff *backoff = [_backoffMap objectForKey:peer.location]; - [backoffs appendFormat:@"[%@ : %@], ", peer.location, backoff.retryTime]; - } - - DSLog(@"[OBJ-C] CoinJoin: backoffs after sorting: %@", backoffs); - } -} - - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.lastSeenBlock = self.chain.lastSyncBlock.height; - DSLog(@"[OBJ-C] CoinJoin peers: new block found, restarting masternode connections job"); + DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); [self triggerConnections]; } } diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 43de7591d..d52c101d3 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1549,6 +1549,10 @@ - (BOOL)transactionIsValid:(DSTransaction *)transaction { } - (BOOL)isSpent:(NSValue *)output { + if (!output) { + return false; + } + return [self.spentOutputs containsObject:output]; } From f9e21b8e6fcf1962fa0195afa1ee6bc3c449517a Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 27 Sep 2024 14:59:00 +0700 Subject: [PATCH 051/133] feat: coincontrol improvements --- DashSync/shared/DashSync.m | 4 +- .../shared/Models/CoinJoin/DSCoinControl.h | 1 + .../shared/Models/CoinJoin/DSCoinControl.m | 7 ++ .../Models/CoinJoin/DSCoinJoinManager.h | 7 +- .../Models/CoinJoin/DSCoinJoinManager.m | 83 +++++++++++++------ .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 +- .../Models/CoinJoin/DSCoinJoinWrapper.m | 18 ++-- .../Chain Managers/DSTransactionManager.m | 14 +++- 8 files changed, 99 insertions(+), 37 deletions(-) diff --git a/DashSync/shared/DashSync.m b/DashSync/shared/DashSync.m index 25d8f807b..030364cc0 100644 --- a/DashSync/shared/DashSync.m +++ b/DashSync/shared/DashSync.m @@ -19,6 +19,7 @@ #import "DSPeerEntity+CoreDataClass.h" #import "DSPeerManager+Protected.h" #import "DSSyncState.h" +#import "DSCoinJoinManager.h" #import "DSQuorumEntryEntity+CoreDataClass.h" #import "DSQuorumSnapshotEntity+CoreDataClass.h" #import "DSSporkManager+Protected.h" @@ -122,13 +123,14 @@ - (void)startSyncForChain:(DSChain *)chain { - (void)stopSyncAllChains { NSArray *chains = [[DSChainsManager sharedInstance] chains]; for (DSChain *chain in chains) { + [[DSCoinJoinManager sharedInstanceForChain:chain] stop]; [[[DSChainsManager sharedInstance] chainManagerForChain:chain].peerManager disconnect: DSDisconnectReason_ChainWipe]; } } - (void)stopSyncForChain:(DSChain *)chain { NSParameterAssert(chain); - + [[DSCoinJoinManager sharedInstanceForChain:chain] stop]; [[[DSChainsManager sharedInstance] chainManagerForChain:chain] stopSync]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.h b/DashSync/shared/Models/CoinJoin/DSCoinControl.h index 7ab85907c..5e75429fa 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.h @@ -44,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isSelected:(DSUTXO)utxo; - (void)useCoinJoin:(BOOL)useCoinJoin; - (BOOL)isUsingCoinJoin; +- (void)select:(DSUTXO)utxo; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.m b/DashSync/shared/Models/CoinJoin/DSCoinControl.m index cf7411e90..48cf94395 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.m @@ -88,4 +88,11 @@ - (BOOL)isUsingCoinJoin { return self.coinType == CoinType_OnlyFullyMixed; } +- (void)select:(DSUTXO)utxo { + NSValue *utxoValue = [NSValue valueWithBytes:&utxo objCType:@encode(DSUTXO)]; + if (![self.setSelected containsObject:utxoValue]) { + [self.setSelected addObject:utxoValue]; + } +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index aadedba2f..d44bec116 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)sessionStartedWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)sessionCompleteWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)mixingStarted; -- (void)mixingComplete:(BOOL)withError; +- (void)mixingComplete:(BOOL)withError isInterrupted:(BOOL)isInterrupted; - (void)transactionProcessedWithId:(UInt256)txId type:(CoinJoinTransactionType)type; @end @@ -84,6 +84,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; - (double)getMixingProgress; +- (DSCoinControl *)selectCoinJoinUTXOs; - (uint64_t)getSmallestDenomination; - (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; @@ -94,8 +95,8 @@ NS_ASSUME_NONNULL_BEGIN // Events - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; -- (void)onMixingStarted:(NSArray *)statuses; -- (void)onMixingComplete:(NSArray *)statuses; +- (void)onMixingStarted:(nonnull NSArray *)statuses; +- (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted; - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type; @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index f6d986682..445cac2dd 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -105,18 +105,22 @@ - (CoinJoinClientOptions *)createOptions { } - (void)updateOptionsWithAmount:(uint64_t)amount { - self.options->coinjoin_amount = amount; - - if (self.wrapper.isRegistered) { - [self.wrapper updateOptions:self.options]; + if (self.options->coinjoin_amount != amount) { + self.options->coinjoin_amount = amount; + + if (self.wrapper.isRegistered) { + [self.wrapper updateOptions:self.options]; + } } } - (void)updateOptionsWithEnabled:(BOOL)isEnabled { - self.options->enable_coinjoin = isEnabled; - - if (self.wrapper.isRegistered) { - [self.wrapper updateOptions:self.options]; + if (self.options->enable_coinjoin != isEnabled) { + self.options->enable_coinjoin = isEnabled; + + if (self.wrapper.isRegistered) { + [self.wrapper updateOptions:self.options]; + } } } @@ -137,9 +141,9 @@ - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessio - (BOOL)isChainSynced { BOOL isSynced = self.chain.chainManager.isSynced; - if (!isSynced) { - [self.chain.chainManager startSync]; - } +// if (!isSynced) { +// [self.chain.chainManager startSync]; +// } return isSynced; } @@ -200,18 +204,20 @@ - (void)doMaintenance { } - (BOOL)startMixing { - DSLog(@"[OBJ-C] CoinJoin: mixing progress: %f", [self getMixingProgress]); self.isMixing = true; return [self.wrapper startMixing]; } - (void)stop { - DSLog(@"[OBJ-C] CoinJoinManager stopping"); - [self cancelCoinjoinTimer]; - self.isMixing = false; - self.cachedLastSuccessBlock = 0; - [self.wrapper stopAndResetClientManager]; - [self stopAsync]; + if (self.isMixing) { + DSLog(@"[OBJ-C] CoinJoinManager stopping"); + self.isMixing = false; + [self cancelCoinjoinTimer]; + self.cachedLastSuccessBlock = 0; + [self updateOptionsWithEnabled:NO]; + [self.wrapper stopAndResetClientManager]; + [self stopAsync]; + } } - (void)stopAsync { @@ -583,6 +589,10 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { } - (double)getMixingProgress { + if (![self.wrapper isRegistered]) { + [self.wrapper registerCoinJoin:self.options]; + } + double requiredRounds = self.options->coinjoin_rounds + 0.875; // 1 x 50% + 1 x 50%^2 + 1 x 50%^3 __block int totalInputs = 0; __block int totalRounds = 0; @@ -870,7 +880,9 @@ - (int32_t)getActiveSessionCount { NSArray *statuses = [self.wrapper getSessionStatuses]; for (NSNumber *status in statuses) { - if (status == PoolStatus_Connecting || status == PoolStatus_Connected || status == PoolStatus_Mixing) { + int statusInt = [status intValue]; + + if (statusInt == PoolStatus_Connecting || statusInt == PoolStatus_Connected || statusInt == PoolStatus_Mixing) { result += 1; } } @@ -878,6 +890,29 @@ - (int32_t)getActiveSessionCount { return result; } +- (DSCoinControl *)selectCoinJoinUTXOs { + DSCoinControl *coinControl = [[DSCoinControl alloc] init]; + [coinControl useCoinJoin:YES]; + NSArray *utxos = self.chain.wallets.firstObject.unspentOutputs; + + for (NSValue *value in utxos) { + DSUTXO utxo; + [value getValue:&utxo]; + + DSTransaction *tx = [self.chain transactionForHash:utxo.hash]; + if (!tx) continue; + + DSTransactionOutput *output = tx.outputs[utxo.n]; + if (!output) continue; + + if ([self isCoinJoinOutput:output utxo:utxo] && ![self.wrapper isLockedCoin:utxo]) { + [coinControl select:utxo]; + } + } + + return coinControl; +} + // Events - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { @@ -887,17 +922,16 @@ - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denom - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { DSLog(@"[OBJ-C] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); - DSLog(@"[OBJ-C] CoinJoin: mixing progress: %f", [self getMixingProgress]); [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onMixingStarted:(nonnull NSArray *)statuses { - DSLog(@"[OBJ-C] CoinJoin: onMixingStarted: %@", statuses); + DSLog(@"[OBJ-C] CoinJoin: onMixingStarted, statuses: %@", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); [self.managerDelegate mixingStarted]; } -- (void)onMixingComplete:(nonnull NSArray *)statuses { - DSLog(@"[OBJ-C] CoinJoin: onMixingComplete: %@", statuses); +- (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted { + DSLog(@"[OBJ-C] CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); BOOL isError = NO; for (NSNumber *statusNumber in statuses) { @@ -911,12 +945,11 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses { } } - [self.managerDelegate mixingComplete:isError]; + [self.managerDelegate mixingComplete:isError isInterrupted:isInterrupted]; } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { DSLog(@"[OBJ-C] CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); - DSLog(@"[OBJ-C] CoinJoin: mixing progress: %f", [self getMixingProgress]); [self.managerDelegate transactionProcessedWithId:txId type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index b8c1de0a8..5cb2b1e47 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -52,10 +52,10 @@ NS_ASSUME_NONNULL_BEGIN - (uint64_t)getCollateralAmount; - (uint32_t)amountToDenomination:(uint64_t)amount; - (int32_t)getRealOutpointCoinJoinRounds:(DSUTXO)utxo; +- (BOOL)isLockedCoin:(DSUTXO)utxo; - (void)stopAndResetClientManager; - (NSArray *)getSessionStatuses; - @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 42aa373ba..b806b69a2 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -58,7 +58,7 @@ - (BOOL)isRegistered { - (void)updateOptions:(CoinJoinClientOptions *)options { @synchronized (self) { - change_coinjoin_options(_clientManager, options); + change_coinjoin_options(self.clientManager, options); } } @@ -240,9 +240,15 @@ - (int32_t)getRealOutpointCoinJoinRounds:(DSUTXO)utxo { } } +- (BOOL)isLockedCoin:(DSUTXO)utxo { + @synchronized (self) { + return is_locked_coin_with_manager(self.clientManager, (uint8_t (*)[32])(utxo.hash.u8), (uint32_t)utxo.n); + } +} + - (void)stopAndResetClientManager { @synchronized (self) { - stop_and_reset_coinjoin(_clientManager); + stop_and_reset_coinjoin(self.clientManager); } } @@ -252,7 +258,8 @@ - (DSChain *)chain { - (void)dealloc { @synchronized (self) { - unregister_client_manager(_clientManager); + unregister_client_manager(self.clientManager); + _clientManager = NULL; } } @@ -604,6 +611,7 @@ void sessionLifecycleListener(bool is_complete, } void mixingLifecycleListener(bool is_complete, + bool is_interrupted, const enum PoolStatus *pool_statuses, uintptr_t pool_statuses_len, const void *context) { @@ -614,8 +622,8 @@ void mixingLifecycleListener(bool is_complete, [statuses addObject:@(pool_statuses[i])]; } - if (is_complete) { - [AS_OBJC(context).manager onMixingComplete:statuses]; + if (is_complete || is_interrupted) { + [AS_OBJC(context).manager onMixingComplete:statuses isInterrupted:is_interrupted]; } else { [AS_OBJC(context).manager onMixingStarted:statuses]; } diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 9c2cd630a..079bdaf29 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -53,6 +53,7 @@ #import "DSTransactionHashEntity+CoreDataClass.h" #import "DSTransactionInput.h" #import "DSTransition.h" +#import "DSCoinJoinManager.h" #import "DSWallet+Protected.h" #import "NSData+Dash.h" #import "NSDate+Utils.h" @@ -582,15 +583,24 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u errorNotificationBlock(error, errorTitle, errorMessage, YES); return; } + + DSCoinControl *coinControl = nil; + DSCoinJoinManager *coinJoinManager = [DSCoinJoinManager sharedInstanceForChain:self.chain]; + + if (coinJoinManager.isMixing) { + coinControl = [coinJoinManager selectCoinJoinUTXOs]; + } if (requestedAmount == 0) { tx = [account transactionForAmounts:protoReq.details.outputAmounts toOutputScripts:protoReq.details.outputScripts - withFee:YES]; + withFee:YES + coinControl:coinControl]; } else if (amount <= account.balance) { tx = [account transactionForAmounts:@[@(requestedAmount)] toOutputScripts:@[protoReq.details.outputScripts.firstObject] - withFee:YES]; + withFee:YES + coinControl:coinControl]; } if (tx) { From f43c4118399ae3ca3643fc54cab71a84b4bafe7a Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 29 Sep 2024 19:41:09 +0700 Subject: [PATCH 052/133] fix: MasternodeGroup crashes --- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index eea324e0e..4cb0d12fc 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -43,9 +43,10 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) NSMutableDictionary *sessionMap; @property (nonatomic, strong) NSMutableDictionary *addressMap; @property (atomic, readonly) NSUInteger maxConnections; -@property (nonatomic, strong) NSMutableArray *pendingClosingMasternodes; +@property (nonatomic, strong) NSMutableArray *mutablePendingClosingMasternodes; @property (nonatomic, strong) NSMutableSet *mutableConnectedPeers; @property (nonatomic, strong) NSMutableSet *mutablePendingPeers; +@property (nonatomic, strong) NSObject *peersLock; @property (nonatomic, readonly) BOOL shouldSendDsq; @property (nullable, nonatomic, readwrite) DSPeer *downloadPeer; @property (nonatomic, strong) DSBackoff *groupBackoff; @@ -63,7 +64,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _coinJoinManager = manager; _chain = manager.chain; _pendingSessions = [NSMutableSet set]; - _pendingClosingMasternodes = [NSMutableArray array]; + _mutablePendingClosingMasternodes = [NSMutableArray array]; _masternodeMap = [NSMutableDictionary dictionary]; _sessionMap = [NSMutableDictionary dictionary]; _addressMap = [NSMutableDictionary dictionary]; @@ -117,8 +118,8 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); - @synchronized (self.pendingClosingMasternodes) { - [self.pendingClosingMasternodes addObject:peer]; + @synchronized (self.mutablePendingClosingMasternodes) { + [self.mutablePendingClosingMasternodes addObject:peer]; // TODO (dashj): what if this disconnects the wrong one [self updateMaxConnections]; } @@ -131,7 +132,6 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(BOOL (^)(DSPeer *peer))predicate { NSMutableString *listOfPeers = [NSMutableString string]; - NSSet *peers = self.connectedPeers; for (DSPeer *peer in peers) { @@ -165,17 +165,23 @@ - (BOOL)isNodePending:(UInt128)ip port:(uint16_t)port { } - (NSSet *)connectedPeers { - @synchronized(self.mutableConnectedPeers) { + @synchronized(self.peersLock) { return [self.mutableConnectedPeers copy]; } } - (NSSet *)pendingPeers { - @synchronized(self.mutablePendingPeers) { + @synchronized(self.peersLock) { return [self.mutablePendingPeers copy]; } } +- (NSMutableArray *)pendingClosingMasternodes { + @synchronized(self.mutablePendingClosingMasternodes) { + return [self.mutablePendingClosingMasternodes copy]; + } +} + - (void)triggerConnections { [self triggerConnectionsJobWithDelay:0]; } @@ -245,11 +251,10 @@ - (DSPeer *)getNextPendingMasternode { DSPeer *peer = [self peerForLocation:ipAddress port:port]; if (peer == nil) { - DSLog(@"[OBJ-C] CoinJoin: not found for %@, creating new", [self hostFor:ipAddress]); peer = [DSPeer peerWithAddress:ipAddress andPort:port onChain:self.chain]; } - if (![self.pendingClosingMasternodes containsObject:peer] && ![self isNodeConnected:peer] && ![self isNodePending:peer]) { + if (![self.mutablePendingClosingMasternodes containsObject:peer] && ![self isNodeConnected:peer] && ![self isNodePending:peer]) { DSBackoff *backoff = [self.backoffMap objectForKey:peer.location]; if (!backoff) { @@ -323,11 +328,10 @@ - (void)updateMaxConnections:(NSUInteger)connections { // We may now have too many or too few open connections. Add more or drop some to get to the right amount. NSInteger adjustment = 0; - NSSet *connectedPeers = self.connectedPeers; - @synchronized (self.mutablePendingPeers) { + @synchronized (self.peersLock) { NSUInteger pendingCount = self.mutablePendingPeers.count; - NSUInteger connectedCount = connectedPeers.count; + NSUInteger connectedCount = self.mutableConnectedPeers.count; NSUInteger numPeers = pendingCount + connectedCount; adjustment = self.maxConnections - numPeers; DSLog(@"[OBJ-C] CoinJoin: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); @@ -373,7 +377,7 @@ - (void)checkMasternodesWithoutSessions { for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; DSLog(@"[OBJ-C] CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); - [self.pendingClosingMasternodes addObject:peer]; + [self.mutablePendingClosingMasternodes addObject:peer]; [peer disconnect]; } } @@ -430,7 +434,7 @@ - (BOOL)connectTo:(DSPeer *)peer { [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.chain.chainManager.governanceSyncManager sporkDelegate:self.chain.chainManager.sporkManager masternodeDelegate:self.chain.chainManager.masternodeManager queue:self.networkingQueue]; peer.earliestKeyTime = self.chain.earliestWalletCreationTime;; - @synchronized (self.mutablePendingPeers) { + @synchronized (self.peersLock) { [self.mutablePendingPeers addObject:peer]; } @@ -440,8 +444,8 @@ - (BOOL)connectTo:(DSPeer *)peer { } - (BOOL)isMasternodeSessionByPeer:(DSPeer *)peer { - @synchronized (_addressMap) { - return [_addressMap objectForKey:peer.location] != nil; + @synchronized (self.addressMap) { + return [self.addressMap objectForKey:peer.location] != nil; } } @@ -452,11 +456,9 @@ - (BOOL)isNodeConnected:(DSPeer *)node { } - (BOOL)isNodePending:(DSPeer *)node { - @synchronized (self) { - for (DSPeer *peer in self.mutablePendingPeers) { - if (uint128_eq(node.address, peer.address) && node.port == peer.port) { - return YES; - } + for (DSPeer *peer in self.pendingPeers) { + if (uint128_eq(node.address, peer.address) && node.port == peer.port) { + return YES; } } @@ -464,7 +466,7 @@ - (BOOL)isNodePending:(DSPeer *)node { } - (void)peerConnected:(nonnull DSPeer *)peer { - @synchronized (self) { + @synchronized (self.peersLock) { [self.groupBackoff trackSuccess]; [[self.backoffMap objectForKey:peer.location] trackSuccess]; @@ -480,7 +482,7 @@ - (void)peerConnected:(nonnull DSPeer *)peer { } - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { - @synchronized (self) { + @synchronized (self.peersLock) { [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; @@ -509,13 +511,13 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err if (masternode) { NSString *address = peer.location; - if ([self.pendingClosingMasternodes containsObject:masternode]) { + if ([self.mutablePendingClosingMasternodes containsObject:masternode]) { // if this is part of pendingClosingMasternodes, where we want to close the connection, // we don't want to increase the backoff time [[self.backoffMap objectForKey:address] trackSuccess]; } - [self.pendingClosingMasternodes removeObject:masternode]; + [self.mutablePendingClosingMasternodes removeObject:masternode]; UInt256 proTxHash = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; NSValue *proTxHashKey = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; NSValue *sessionIdObject = [self.masternodeMap objectForKey:proTxHashKey]; From b865fe4369d35ec7a123171c4ce31a759161c68b Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 29 Sep 2024 19:42:10 +0700 Subject: [PATCH 053/133] fix: change state to enabled when starting CoinJoin --- .../Models/CoinJoin/DSCoinJoinManager.h | 2 +- .../Models/CoinJoin/DSCoinJoinManager.m | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index d44bec116..b05ed1bde 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -78,7 +78,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; -- (void)doAutomaticDenominating; +- (void)doAutomaticDenominatingWithReport:(BOOL)report; - (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun; - (void)updateSuccessBlock; - (void)refreshUnusedKeys; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 445cac2dd..d46e728b6 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -139,13 +139,7 @@ - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessio } - (BOOL)isChainSynced { - BOOL isSynced = self.chain.chainManager.isSynced; - -// if (!isSynced) { -// [self.chain.chainManager startSync]; -// } - - return isSynced; + return self.chain.chainManager.isSynced; } - (void)startAsync { @@ -173,7 +167,14 @@ - (void)start { @synchronized (self) { self.cachedBlockHeight = self.chain.lastSyncBlock.height; - [self.wrapper registerCoinJoin:self.options]; + self.options->enable_coinjoin = YES; + + if ([self.wrapper isRegistered]) { + [self.wrapper updateOptions:self.options]; + } else { + [self.wrapper registerCoinJoin:self.options]; + } + self.coinjoinTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.processingQueue); if (self.coinjoinTimer) { dispatch_source_set_timer(self.coinjoinTimer, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), interval * NSEC_PER_SEC, 1ull * NSEC_PER_SEC); @@ -259,7 +260,7 @@ - (void)handleTransactionReceivedNotification { } } -- (void)doAutomaticDenominating { +- (void)doAutomaticDenominatingWithReport:(BOOL)report { if ([self validMNCount] == 0) { DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); return; @@ -267,7 +268,11 @@ - (void)doAutomaticDenominating { dispatch_async(self.processingQueue, ^{ DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); - [self.wrapper doAutomaticDenominatingWithDryRun:NO]; + BOOL result = [self.wrapper doAutomaticDenominatingWithDryRun:NO]; + + if (report) { + DSLog(@"[OBJ-C] CoinJoin: Mixing %@", result ? @"started successfully" : @"start failed, will retry"); + } }); } From de6320db317b4b025f4e7e62192fed014f18ea14 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 1 Oct 2024 22:45:49 +0300 Subject: [PATCH 054/133] fix: xcode 16 build --- Example/Podfile | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Example/Podfile b/Example/Podfile index e5a8d4466..f25dfc824 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -26,17 +26,21 @@ target 'NetworkInfo' do end post_install do |installer| - installer.pods_project.targets.each do |target| - # fixes warnings about unsupported Deployment Target in Xcode 10 - target.build_configurations.each do |config| - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' - end - # Hide warnings for specific pods - if ["gRPC"].include? target.name - target.build_configurations.each do |config| - config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = 'YES' - end + installer.pods_project.targets.each do |target| + # fixes warnings about unsupported Deployment Target in Xcode 10 + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + end + + # Ensure the GCC_WARN_INHIBIT_ALL_WARNINGS flag is removed for BoringSSL-GRPC and BoringSSL-GRPC-iOS + if ['BoringSSL-GRPC', 'BoringSSL-GRPC-iOS'].include? target.name + target.source_build_phase.files.each do |file| + if file.settings && file.settings['COMPILER_FLAGS'] + flags = file.settings['COMPILER_FLAGS'].split + flags.reject! { |flag| flag == '-GCC_WARN_INHIBIT_ALL_WARNINGS' } + file.settings['COMPILER_FLAGS'] = flags.join(' ') + end end + end end -end - +end \ No newline at end of file From b6db74b5f875cfa417637628e96d8809f4833b06 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 3 Oct 2024 15:14:09 +0700 Subject: [PATCH 055/133] fix: set from the caller to account for coinjoin being enabled, but not mixing --- .../DSFriendRequestEntity+CoreDataClass.m | 1 + .../Chain Managers/DSTransactionManager.h | 2 +- .../Chain Managers/DSTransactionManager.m | 23 +++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m index 0975c5589..65cb2091c 100644 --- a/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m @@ -88,6 +88,7 @@ - (void)sendAmount:(uint64_t)amount fromAccount:(DSAccount *)account requestingA NSAssert([paymentRequest isValidAsNonDashpayPaymentRequest], @"Payment request must be valid"); + // TODO: mixed only? [account.wallet.chain.chainManager.transactionManager confirmPaymentRequest:paymentRequest usingUserBlockchainIdentity:nil fromAccount:account diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h index 886cda483..afbb89548 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h @@ -98,7 +98,7 @@ typedef void (^DSTransactionRequestRelayCompletionBlock)(DSTransaction *tx, DSPa requestRelayCompletion:(DSTransactionRequestRelayCompletionBlock _Nullable)requestRelayCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock; -- (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(uint64_t)requestedAmount fromAccount:(DSAccount *)account acceptInternalAddress:(BOOL)acceptInternalAddress acceptReusingAddress:(BOOL)acceptReusingAddress addressIsFromPasteboard:(BOOL)addressIsFromPasteboard acceptUncertifiedPayee:(BOOL)acceptUncertifiedPayee requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt +- (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(uint64_t)requestedAmount fromAccount:(DSAccount *)account acceptInternalAddress:(BOOL)acceptInternalAddress acceptReusingAddress:(BOOL)acceptReusingAddress addressIsFromPasteboard:(BOOL)addressIsFromPasteboard acceptUncertifiedPayee:(BOOL)acceptUncertifiedPayee mixedOnly:(BOOL)mixedOnly requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt 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 079bdaf29..7da3820ce 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -448,6 +448,7 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u acceptReusingAddress:NO addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:NO + mixedOnly:NO requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest @@ -459,7 +460,7 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u errorNotificationBlock:errorNotificationBlock]; } -- (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(uint64_t)requestedAmount fromAccount:(DSAccount *)account acceptInternalAddress:(BOOL)acceptInternalAddress acceptReusingAddress:(BOOL)acceptReusingAddress addressIsFromPasteboard:(BOOL)addressIsFromPasteboard acceptUncertifiedPayee:(BOOL)acceptUncertifiedPayee +- (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(uint64_t)requestedAmount fromAccount:(DSAccount *)account acceptInternalAddress:(BOOL)acceptInternalAddress acceptReusingAddress:(BOOL)acceptReusingAddress addressIsFromPasteboard:(BOOL)addressIsFromPasteboard acceptUncertifiedPayee:(BOOL)acceptUncertifiedPayee mixedOnly:(BOOL)mixedOnly requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest @@ -507,7 +508,7 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u NSString *challengeAction = DSLocalizedString(@"Ignore", nil); challenge( challengeTitle, challengeMessage, challengeAction, ^{ - [self confirmProtocolRequest:protoReq forAmount:requestedAmount fromAccount:account acceptInternalAddress:YES acceptReusingAddress:acceptReusingAddress addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:acceptUncertifiedPayee requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; + [self confirmProtocolRequest:protoReq forAmount:requestedAmount fromAccount:account acceptInternalAddress:YES acceptReusingAddress:acceptReusingAddress addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:acceptUncertifiedPayee mixedOnly:mixedOnly requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; }, ^{ additionalInfoRequest(DSRequestingAdditionalInfo_CancelOrChangeAmount); @@ -529,6 +530,7 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u acceptReusingAddress:YES addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:acceptUncertifiedPayee + mixedOnly:mixedOnly requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest @@ -550,7 +552,7 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u NSString *challengeAction = DSLocalizedString(@"Ignore", nil); challenge( challengeTitle, challengeMessage, challengeAction, ^{ - [self confirmProtocolRequest:protoReq forAmount:requestedAmount fromAccount:account acceptInternalAddress:acceptInternalAddress acceptReusingAddress:acceptReusingAddress addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:YES requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; + [self confirmProtocolRequest:protoReq forAmount:requestedAmount fromAccount:account acceptInternalAddress:acceptInternalAddress acceptReusingAddress:acceptReusingAddress addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:YES mixedOnly:mixedOnly requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; }, ^{ additionalInfoRequest(DSRequestingAdditionalInfo_CancelOrChangeAmount); @@ -585,10 +587,9 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u } DSCoinControl *coinControl = nil; - DSCoinJoinManager *coinJoinManager = [DSCoinJoinManager sharedInstanceForChain:self.chain]; - if (coinJoinManager.isMixing) { - coinControl = [coinJoinManager selectCoinJoinUTXOs]; + if (mixedOnly) { + coinControl = [[DSCoinJoinManager sharedInstanceForChain:self.chain] selectCoinJoinUTXOs]; } if (requestedAmount == 0) { @@ -644,14 +645,14 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u } } -- (void)signAndPublishTransaction:(DSTransaction *)tx createdFromProtocolRequest:(DSPaymentProtocolRequest *)protocolRequest fromAccount:(DSAccount *)account toAddress:(NSString *)address requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt promptMessage:(NSString *)promptMessage forAmount:(uint64_t)amount keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest presentChallenge:(DSTransactionChallengeBlock)challenge transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion signedCompletion:(DSTransactionSigningCompletionBlock)signedCompletion publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion requestRelayCompletion:(DSTransactionRequestRelayCompletionBlock)requestRelayCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock { +- (void)signAndPublishTransaction:(DSTransaction *)tx createdFromProtocolRequest:(DSPaymentProtocolRequest *)protocolRequest fromAccount:(DSAccount *)account toAddress:(NSString *)address requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt promptMessage:(NSString *)promptMessage forAmount:(uint64_t)amount keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication mixedOnly:(BOOL)mixedOnly requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest presentChallenge:(DSTransactionChallengeBlock)challenge transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion signedCompletion:(DSTransactionSigningCompletionBlock)signedCompletion publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion requestRelayCompletion:(DSTransactionRequestRelayCompletionBlock)requestRelayCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock { DSAuthenticationManager *authenticationManager = [DSAuthenticationManager sharedInstance]; __block BOOL previouslyWasAuthenticated = authenticationManager.didAuthenticate; if (!tx) { // tx is nil if there were insufficient wallet funds if (authenticationManager.didAuthenticate) { //the fee puts us over the limit - [self insufficientFundsForTransactionCreatedFromProtocolRequest:protocolRequest fromAccount:account forAmount:amount toAddress:address requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; + [self insufficientFundsForTransactionCreatedFromProtocolRequest:protocolRequest fromAccount:account forAmount:amount toAddress:address requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication mixedOnly:mixedOnly requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; } else { [authenticationManager seedWithPrompt:promptMessage forWallet:account.wallet @@ -660,7 +661,7 @@ - (void)signAndPublishTransaction:(DSTransaction *)tx createdFromProtocolRequest completion:^(NSData *_Nullable seed, BOOL cancelled) { if (seed) { //the fee puts us over the limit - [self insufficientFundsForTransactionCreatedFromProtocolRequest:protocolRequest fromAccount:account forAmount:amount toAddress:address requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; + [self insufficientFundsForTransactionCreatedFromProtocolRequest:protocolRequest fromAccount:account forAmount:amount toAddress:address requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication mixedOnly:mixedOnly requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; } else { additionalInfoRequest(DSRequestingAdditionalInfo_CancelOrChangeAmount); } @@ -810,6 +811,7 @@ - (void)publishSignedTransaction:(DSTransaction *)tx createdFromProtocolRequest: - (void)insufficientFundsForTransactionCreatedFromProtocolRequest:(DSPaymentProtocolRequest *)protocolRequest fromAccount:(DSAccount *)account forAmount:(uint64_t)requestedSendAmount toAddress:(NSString *)address requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication + mixedOnly:(BOOL)mixedOnly requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest presentChallenge:(DSTransactionChallengeBlock)challenge transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion @@ -844,6 +846,7 @@ - (void)insufficientFundsForTransactionCreatedFromProtocolRequest:(DSPaymentProt acceptReusingAddress:YES addressIsFromPasteboard:NO acceptUncertifiedPayee:YES + mixedOnly:mixedOnly requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest @@ -883,7 +886,7 @@ - (void)confirmPaymentRequest:(DSPaymentRequest *)paymentRequest usingUserBlockc publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock { DSPaymentProtocolRequest *protocolRequest = [paymentRequest protocolRequestForBlockchainIdentity:blockchainIdentity 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]; + [self confirmProtocolRequest:protocolRequest forAmount:paymentRequest.amount fromAccount:account acceptInternalAddress:acceptInternalAddress acceptReusingAddress:acceptReusingAddress addressIsFromPasteboard:addressIsFromPasteboard acceptUncertifiedPayee:NO mixedOnly:NO requiresSpendingAuthenticationPrompt:requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:nil errorNotificationBlock:errorNotificationBlock]; } // MARK: - Mempools Sync From 03698d4a7b17cda10bfbbd104fd7557fddf13b76 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 3 Oct 2024 15:41:38 +0700 Subject: [PATCH 056/133] fix: stuck syncing --- .../Models/CoinJoin/DSCoinJoinManager.m | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index d46e728b6..91c4a957e 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -55,7 +55,7 @@ @implementation DSCoinJoinManager + (instancetype)sharedInstanceForChain:(DSChain *)chain { NSParameterAssert(chain); - + dispatch_once(&managerChainToken, ^{ _managerChainDictionary = [NSMutableDictionary dictionary]; }); @@ -132,14 +132,20 @@ - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessio self.options->coinjoin_multi_session = multisession; self.options->coinjoin_denoms_goal = denomGoal; self.options->coinjoin_denoms_hardcap = denomHardCap; - + if (self.wrapper.isRegistered) { [self.wrapper updateOptions:self.options]; } } - (BOOL)isChainSynced { - return self.chain.chainManager.isSynced; + BOOL isSynced = self.chain.chainManager.syncPhase == DSChainSyncPhase_Synced; + + if (!isSynced) { + DSLog(@"[OBJ-C] CoinJoin: isSynced: %@, combinedSyncProgress: %d, syncState: %@", isSynced ? @"YES" : @"NO", self.chain.chainManager.combinedSyncProgress, self.chain.chainManager.syncState); + } + + return isSynced; } - (void)startAsync { @@ -189,12 +195,12 @@ - (void)start { - (void)doMaintenance { // TODO: // report masternode group -// if (masternodeGroup != null) { -// tick++; -// if (tick % 15 == 0) { -// log.info(masternodeGroup.toString()); -// } -// } + // if (masternodeGroup != null) { + // tick++; + // if (tick % 15 == 0) { + // log.info(masternodeGroup.toString()); + // } + // } if ([self validMNCount] == 0) { DSLog(@"[OBJ-C] CoinJoin doMaintenance: No Masternodes detected."); @@ -222,11 +228,11 @@ - (void)stop { } - (void)stopAsync { - if (self.masternodeGroup != nil && self.masternodeGroup.isRunning) { - DSLog(@"[OBJ-C] CoinJoinManager stopAsync"); - [self.chain.chainManager.peerManager shouldSendDsq:false]; - [self.masternodeGroup stopAsync]; - self.masternodeGroup = nil; + if (self.masternodeGroup != nil && self.masternodeGroup.isRunning) { + DSLog(@"[OBJ-C] CoinJoinManager stopAsync"); + [self.chain.chainManager.peerManager shouldSendDsq:false]; + [self.masternodeGroup stopAsync]; + self.masternodeGroup = nil; } [[NSNotificationCenter defaultCenter] removeObserver:self]; @@ -324,7 +330,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { if (index < tx.outputs.count) { DSTransactionOutput *output = tx.outputs[index]; - if ([account containsAddress:output.address]) { // TODO: is it the same as isPubKeyMine? + if ([account containsAddress:output.address]) { return YES; } } @@ -712,7 +718,7 @@ - (DSCoinJoinBalance *)getBalance { } } - // TODO: support more balance types? + // TODO(DashJ): support more balance types? DSCoinJoinBalance *balance = [DSCoinJoinBalance balanceWithMyTrusted:self.chain.balance denominatedTrusted:denominatedBalance From 26e9aedd385c018e5a90e3c464e11d9f94d30d7e Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 7 Oct 2024 19:24:50 +0700 Subject: [PATCH 057/133] fix: take parameter from the caller --- .../Models/Managers/Chain Managers/DSTransactionManager.h | 2 +- .../Models/Managers/Chain Managers/DSTransactionManager.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h index afbb89548..8912d57fa 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.h @@ -86,7 +86,7 @@ typedef void (^DSTransactionRequestRelayCompletionBlock)(DSTransaction *tx, DSPa publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock; -- (void)signAndPublishTransaction:(DSTransaction *)tx createdFromProtocolRequest:(DSPaymentProtocolRequest *)protocolRequest fromAccount:(DSAccount *)account toAddress:(NSString *)address requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingConfirmationPrompt promptMessage:(NSString *_Nullable)promptMessage forAmount:(uint64_t)amount keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest presentChallenge:(DSTransactionChallengeBlock)challenge transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion signedCompletion:(DSTransactionSigningCompletionBlock)signedCompletion publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion requestRelayCompletion:(DSTransactionRequestRelayCompletionBlock _Nullable)requestRelayCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock; +- (void)signAndPublishTransaction:(DSTransaction *)tx createdFromProtocolRequest:(DSPaymentProtocolRequest *)protocolRequest fromAccount:(DSAccount *)account toAddress:(NSString *)address requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingConfirmationPrompt promptMessage:(NSString *_Nullable)promptMessage forAmount:(uint64_t)amount keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication mixedOnly:(BOOL)mixedOnly requestingAdditionalInfo:(DSTransactionCreationRequestingAdditionalInfoBlock)additionalInfoRequest presentChallenge:(DSTransactionChallengeBlock)challenge transactionCreationCompletion:(DSTransactionCreationCompletionBlock)transactionCreationCompletion signedCompletion:(DSTransactionSigningCompletionBlock)signedCompletion publishedCompletion:(DSTransactionPublishedCompletionBlock)publishedCompletion requestRelayCompletion:(DSTransactionRequestRelayCompletionBlock _Nullable)requestRelayCompletion errorNotificationBlock:(DSTransactionErrorNotificationBlock)errorNotificationBlock; - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(uint64_t)requestedAmount fromAccount:(DSAccount *)account addressIsFromPasteboard:(BOOL)addressIsFromPasteboard requiresSpendingAuthenticationPrompt:(BOOL)requiresSpendingAuthenticationPrompt keepAuthenticatedIfErrorAfterAuthentication:(BOOL)keepAuthenticatedIfErrorAfterAuthentication diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 7da3820ce..2082d7d1c 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -640,7 +640,7 @@ - (void)confirmProtocolRequest:(DSPaymentProtocolRequest *)protoReq forAmount:(u if (transactionCreationCompletion(tx, suggestedPrompt, amount, fee, address ? @[address] : @[], isSecure)) { CFRunLoopPerformBlock([[NSRunLoop mainRunLoop] getCFRunLoop], kCFRunLoopCommonModes, ^{ - [self signAndPublishTransaction:tx createdFromProtocolRequest:protoReq fromAccount:account toAddress:address requiresSpendingAuthenticationPrompt:YES promptMessage:suggestedPrompt forAmount:amount keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; + [self signAndPublishTransaction:tx createdFromProtocolRequest:protoReq fromAccount:account toAddress:address requiresSpendingAuthenticationPrompt:YES promptMessage:suggestedPrompt forAmount:amount keepAuthenticatedIfErrorAfterAuthentication:keepAuthenticatedIfErrorAfterAuthentication mixedOnly:mixedOnly requestingAdditionalInfo:additionalInfoRequest presentChallenge:challenge transactionCreationCompletion:transactionCreationCompletion signedCompletion:signedCompletion publishedCompletion:publishedCompletion requestRelayCompletion:requestRelayCompletion errorNotificationBlock:errorNotificationBlock]; }); } } From c15ff1dacc5fb6c237d83b7828390e871716db1f Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 11 Oct 2024 18:58:53 +0700 Subject: [PATCH 058/133] chore: reduce logs --- .../Models/CoinJoin/DSCoinJoinManager.m | 71 ++++++++++--------- .../Models/CoinJoin/DSCoinJoinWrapper.m | 12 ++-- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 44 ++++++------ DashSync/shared/Models/Network/DSPeer.m | 4 -- DashSync/shared/Models/Wallet/DSAccount.m | 1 - 5 files changed, 62 insertions(+), 70 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 91c4a957e..92c55c8d0 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -45,6 +45,7 @@ @interface DSCoinJoinManager () @property (nonatomic, strong) dispatch_source_t coinjoinTimer; @property (atomic) int32_t cachedLastSuccessBlock; @property (atomic) int32_t cachedBlockHeight; // Keep track of current block height +@property (atomic) double lastReportedProgress; @end @@ -79,7 +80,9 @@ - (instancetype)initWithChain:(DSChain *)chain { _processingQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.coinjoin.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL); _cachedBlockHeight = 0; _cachedLastSuccessBlock = 0; + _lastReportedProgress = 0; _options = [self createOptions]; + [self printUsedKeys]; } return self; } @@ -125,7 +128,7 @@ - (void)updateOptionsWithEnabled:(BOOL)isEnabled { } - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessions:(int32_t)sessions withMultisession:(BOOL)multisession denominationGoal:(int32_t)denomGoal denominationHardCap:(int32_t)denomHardCap { - DSLog(@"[OBJ-C] CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); + DSLog(@"CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); self.options->coinjoin_amount = amount; self.options->coinjoin_rounds = rounds; self.options->coinjoin_sessions = sessions; @@ -139,13 +142,7 @@ - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessio } - (BOOL)isChainSynced { - BOOL isSynced = self.chain.chainManager.syncPhase == DSChainSyncPhase_Synced; - - if (!isSynced) { - DSLog(@"[OBJ-C] CoinJoin: isSynced: %@, combinedSyncProgress: %d, syncState: %@", isSynced ? @"YES" : @"NO", self.chain.chainManager.combinedSyncProgress, self.chain.chainManager.syncState); - } - - return isSynced; + return self.chain.chainManager.syncPhase == DSChainSyncPhase_Synced; } - (void)startAsync { @@ -159,14 +156,13 @@ - (void)startAsync { name:DSTransactionManagerTransactionReceivedNotification object:nil]; - DSLog(@"[OBJ-C] CoinJoin: broadcasting senddsq(true) to all peers"); [self.chain.chainManager.peerManager shouldSendDsq:true]; [self.masternodeGroup startAsync]; } } - (void)start { - DSLog(@"[OBJ-C] CoinJoinManager starting, time: %@", [NSDate date]); + DSLog(@"CoinJoinManager starting"); [self cancelCoinjoinTimer]; uint32_t interval = 1; uint32_t delay = 1; @@ -203,7 +199,6 @@ - (void)doMaintenance { // } if ([self validMNCount] == 0) { - DSLog(@"[OBJ-C] CoinJoin doMaintenance: No Masternodes detected."); return; } @@ -217,7 +212,7 @@ - (BOOL)startMixing { - (void)stop { if (self.isMixing) { - DSLog(@"[OBJ-C] CoinJoinManager stopping"); + DSLog(@"CoinJoinManager stopping"); self.isMixing = false; [self cancelCoinjoinTimer]; self.cachedLastSuccessBlock = 0; @@ -229,7 +224,6 @@ - (void)stop { - (void)stopAsync { if (self.masternodeGroup != nil && self.masternodeGroup.isRunning) { - DSLog(@"[OBJ-C] CoinJoinManager stopAsync"); [self.chain.chainManager.peerManager shouldSendDsq:false]; [self.masternodeGroup stopAsync]; self.masternodeGroup = nil; @@ -258,41 +252,34 @@ - (void)handleTransactionReceivedNotification { DSTransaction *lastTransaction = wallet.accounts.firstObject.recentTransactions.firstObject; if ([self.wrapper isMixingFeeTx:lastTransaction.txHash]) { - DSLog(@"[OBJ-C] CoinJoin tx: Mixing Fee: %@", uint256_reverse_hex(lastTransaction.txHash)); + DSLog(@"CoinJoin tx: Mixing Fee: %@", uint256_reverse_hex(lastTransaction.txHash)); [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_MixingFee]; } else if ([self coinJoinTxTypeForTransaction:lastTransaction] == CoinJoinTransactionType_Mixing) { - DSLog(@"[OBJ-C] CoinJoin tx: Mixing Transaction: %@", uint256_reverse_hex(lastTransaction.txHash)); + DSLog(@"CoinJoin tx: Mixing Transaction: %@", uint256_reverse_hex(lastTransaction.txHash)); [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; } } - (void)doAutomaticDenominatingWithReport:(BOOL)report { if ([self validMNCount] == 0) { - DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); return; } dispatch_async(self.processingQueue, ^{ - DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); BOOL result = [self.wrapper doAutomaticDenominatingWithDryRun:NO]; if (report) { - DSLog(@"[OBJ-C] CoinJoin: Mixing %@", result ? @"started successfully" : @"start failed, will retry"); + DSLog(@"CoinJoin: Mixing %@", result ? @"started successfully" : @"start failed, will retry"); } }); } - (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { - if ([self validMNCount] == 0) { - DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); - return false; - } - if (![self.wrapper isRegistered]) { [self.wrapper registerCoinJoin:self.options]; } - DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); + DSLog(@"CoinJoin: doAutomaticDenominatingWithDryRun: %@", dryRun ? @"YES" : @"NO"); return [self.wrapper doAutomaticDenominatingWithDryRun:dryRun]; } @@ -644,7 +631,12 @@ - (double)getMixingProgress { }]; double progress = totalInputs != 0 ? (double)totalRounds / (requiredRounds * totalInputs) : 0.0; - DSLog(@"[OBJ-C] CoinJoin: getMixingProgress: %f = %d / (%f * %d)", progress, totalRounds, requiredRounds, totalInputs); + + if (self.lastReportedProgress != progress) { + _lastReportedProgress = progress; + DSLog(@"CoinJoin: getMixingProgress: %f = %d / (%f * %d)", progress, totalRounds, requiredRounds, totalInputs); + } + return fmax(0.0, fmin(progress, 1.0)); } @@ -795,14 +787,14 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output BOOL signedTransaction = [account signTransaction:transaction]; if (!signedTransaction || !transaction.isSigned) { - DSLog(@"[OBJ-C] CoinJoin error: not signed"); + DSLog(@"CoinJoin error: not signed"); return NO; } else { [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { if (error) { - DSLog(@"[OBJ-C] CoinJoin publish error: %@ for tx: %@", error.description, transaction.description); + DSLog(@"CoinJoin publish error: %@ for tx: %@", error.description, transaction.description); } else { - DSLog(@"[OBJ-C] CoinJoin publish success: %@", transaction.description); + DSLog(@"CoinJoin publish success: %@", transaction.description); } dispatch_async(self.processingQueue, ^{ @@ -846,7 +838,7 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with DSCoinJoinSignedInputs *request = [DSCoinJoinSignedInputs requestWithData:message]; [peer sendRequest:request]; } else { - DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); + DSLog(@"CoinJoin: unknown message type: %@", messageType); return NO; } @@ -924,25 +916,34 @@ - (DSCoinControl *)selectCoinJoinUTXOs { return coinControl; } +- (void)printUsedKeys { + dispatch_async(self.processingQueue, ^{ + NSArray *issuedAddresses = [self getIssuedReceiveAddresses]; + NSArray *usedAddresses = [self getUsedReceiveAddresses]; + double percent = (double)usedAddresses.count * 100.0 / (double)issuedAddresses.count; + DSLog(@"CoinJoin init. Used addresses count %d out of %d (%.2f %%)", usedAddresses.count, issuedAddresses.count, percent); + }); +} + // Events - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"[OBJ-C] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"[OBJ-C] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onMixingStarted:(nonnull NSArray *)statuses { - DSLog(@"[OBJ-C] CoinJoin: onMixingStarted, statuses: %@", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + DSLog(@"CoinJoin: onMixingStarted, statuses: %@", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); [self.managerDelegate mixingStarted]; } - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted { - DSLog(@"[OBJ-C] CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + DSLog(@"CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); BOOL isError = NO; for (NSNumber *statusNumber in statuses) { @@ -951,7 +952,7 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter status != PoolStatus_ErrNotEnoughFunds && status != PoolStatus_ErrNoInputs) { isError = YES; - DSLog(@"[OBJ-C] CoinJoin: Mixing stopped before completion. Status: %d", status); + DSLog(@"CoinJoin: Mixing stopped before completion. Status: %d", status); break; } } @@ -960,7 +961,7 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { - DSLog(@"[OBJ-C] CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); + DSLog(@"CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); [self.managerDelegate transactionProcessedWithId:txId type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index b806b69a2..23105edff 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -43,10 +43,8 @@ - (instancetype)initWithManagers:(DSCoinJoinManager *)manager chainManager:(DSCh - (void)registerCoinJoin:(CoinJoinClientOptions *)options { @synchronized (self) { if (_clientManager == NULL) { - DSLog(@"[OBJ-C] CoinJoin: register client manager"); _clientManager = register_client_manager(AS_RUST(self), options, getMNList, destroyMNList, getInputValueByPrevoutHash, hasChainLock, destroyInputValue, updateSuccessBlock, isWaitingForNewBlock, getTransaction, signTransaction, destroyTransaction, isMineInput, commitTransaction, isBlockchainSynced, freshCoinJoinAddress, countInputsWithAmount, availableCoins, destroyGatheredOutputs, selectCoinsGroupedByAddresses, destroySelectedCoins, isMasternodeOrDisconnectRequested, disconnectMasternode, sendMessage, addPendingMasternode, startManagerAsync, sessionLifecycleListener, mixingLifecycleListener, getCoinJoinKeys, destroyCoinJoinKeys); - DSLog(@"[OBJ-C] CoinJoin: register client queue manager"); add_client_queue_manager(_clientManager, masternodeByHash, destroyMasternodeEntry, validMNCount, AS_RUST(self)); } } @@ -449,8 +447,6 @@ ByteArray freshCoinJoinAddress(bool internal, const void *context) { } bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinControl *coinControl, bool is_denominating, uint8_t (*client_session_id)[32], const void *context) { - DSLog(@"[OBJ-C] CoinJoin: commitTransaction"); - NSMutableArray *amounts = [NSMutableArray array]; NSMutableArray *scripts = [NSMutableArray array]; @@ -468,19 +464,19 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinContr DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts coinControl:cc onPublished:^(UInt256 txId, NSError * _Nullable error) { if (error) { - DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); + DSLog(@"CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); } else if (is_denominating) { - DSLog(@"[OBJ-C] CoinJoin tx: Denominations Created: %@", uint256_reverse_hex(txId)); + DSLog(@"CoinJoin tx: Denominations Created: %@", uint256_reverse_hex(txId)); bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); if (!isFinished) { - DSLog(@"[OBJ-C] CoinJoin: auto_denom not finished"); + DSLog(@"CoinJoin: auto_denom not finished"); } processor_destroy_block_hash(client_session_id); [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; } else { - DSLog(@"[OBJ-C] CoinJoin tx: Collateral Created: %@", uint256_reverse_hex(txId)); + DSLog(@"CoinJoin tx: Collateral Created: %@", uint256_reverse_hex(txId)); [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } }]; diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 4cb0d12fc..65f775401 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -116,7 +116,7 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { - DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); + DSLog(@"CoinJoin: masternode[closing] %@", [self hostFor:ip]); @synchronized (self.mutablePendingClosingMasternodes) { [self.mutablePendingClosingMasternodes addObject:peer]; @@ -144,10 +144,10 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B if (warn) { if (![self isNodePending:ip port:port]) { - DSLog(@"[OBJ-C] CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); + DSLog(@"CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); } else { - DSLog(@"[OBJ-C] CoinJoin: %@ in the list of pending peers", [self hostFor:ip]); + DSLog(@"CoinJoin: %@ in the list of pending peers", [self hostFor:ip]); } } @@ -213,7 +213,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; if (delay > 0.1) { - DSLog(@"[OBJ-C] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); + DSLog(@"CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); [self triggerConnectionsJobWithDelay:delay]; return; } @@ -273,7 +273,7 @@ - (DSPeer *)getNextPendingMasternode { if (peerWithLeastBackoff) { [self.addressMap setObject:sessionValueWithLeastBackoff forKey:peerWithLeastBackoff.location]; - DSLog(@"[OBJ-C] CoinJoin: discovery: %@ -> %@", peerWithLeastBackoff.location, uint256_hex(sessionId)); + DSLog(@"CoinJoin: discovery: %@ -> %@", peerWithLeastBackoff.location, uint256_hex(sessionId)); } return peerWithLeastBackoff; @@ -296,7 +296,7 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self.pendingSessions) { - DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); + DSLog(@"CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [self.pendingSessions addObject:sessionIdValue]; @@ -314,7 +314,7 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session - (void)updateMaxConnections { _maxConnections = self.pendingSessions.count; NSUInteger connections = MIN(self.maxConnections, self.coinJoinManager.options->coinjoin_sessions); - DSLog(@"[OBJ-C] CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); + DSLog(@"CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); [self updateMaxConnections:connections]; } @@ -334,7 +334,7 @@ - (void)updateMaxConnections:(NSUInteger)connections { NSUInteger connectedCount = self.mutableConnectedPeers.count; NSUInteger numPeers = pendingCount + connectedCount; adjustment = self.maxConnections - numPeers; - DSLog(@"[OBJ-C] CoinJoin: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); + DSLogPrivate(@"CoinJoin: updateMaxConnections adjustment %lu, pendingCount: %lu, connectedCount: %lu", adjustment, pendingCount, connectedCount); } if (adjustment > 0) { @@ -362,21 +362,21 @@ - (void)checkMasternodesWithoutSessions { } } else { // TODO(DashJ): we may not need this anymore - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); + DSLog(@"CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); } } if (!found) { - DSLog(@"[OBJ-C] CoinJoin: masternode is not connected to a session: %@", peer.location); + DSLog(@"CoinJoin: masternode is not connected to a session: %@", peer.location); [masternodesToDrop addObject:peer]; } } - DSLog(@"[OBJ-C] CoinJoin: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); + DSLogPrivate(@"CoinJoin: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; - DSLog(@"[OBJ-C] CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + DSLog(@"CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); [self.mutablePendingClosingMasternodes addObject:peer]; [peer disconnect]; } @@ -392,15 +392,15 @@ - (NSString *)hostFor:(UInt128)address { } - (BOOL)connectTo:(DSPeer *)peer { - DSLog(@"[OBJ-C] CoinJoin: connectTo: %@", peer.location); + DSLogPrivate(@"CoinJoin: connectTo: %@", peer.location); if (![self isMasternodeSessionByPeer:peer]) { - DSLog(@"[OBJ-C] CoinJoin: %@ not a masternode session, exit", peer.location); + DSLog(@"CoinJoin: %@ not a masternode session, exit", peer.location); return NO; } if ([self isNodeConnected:peer] || [self isNodePending:peer]) { - DSLog(@"[OBJ-C] CoinJoin: attempting to connect to the same masternode again: %@", peer.location); + DSLog(@"CoinJoin: attempting to connect to the same masternode again: %@", peer.location); return NO; // do not connect to the same peer again } @@ -418,18 +418,18 @@ - (BOOL)connectTo:(DSPeer *)peer { } if (uint256_is_zero(sessionId)) { - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap"); + DSLog(@"CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap"); return NO; } DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; if (!mixingMasternodeAddress) { - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, sessionId: %@", uint256_hex(sessionId)); + DSLog(@"CoinJoin: session is not connected to a masternode, sessionId: %@", uint256_hex(sessionId)); return NO; } - DSLog(@"[OBJ-C] CoinJoin: masternode[connecting] %@: %@; %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); + DSLog(@"CoinJoin: masternode[connecting] %@: %@; %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.chain.chainManager.governanceSyncManager sporkDelegate:self.chain.chainManager.sporkManager masternodeDelegate:self.chain.chainManager.masternodeManager queue:self.networkingQueue]; peer.earliestKeyTime = self.chain.earliestWalletCreationTime;; @@ -470,7 +470,7 @@ - (void)peerConnected:(nonnull DSPeer *)peer { [self.groupBackoff trackSuccess]; [[self.backoffMap objectForKey:peer.location] trackSuccess]; - DSLog(@"[OBJ-C] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); + DSLog(@"CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers addObject:peer]; @@ -486,7 +486,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; - DSLog(@"[OBJ-C] CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); + DSLog(@"CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; @@ -506,7 +506,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err } } - DSLog(@"[OBJ-C] CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); if (masternode) { NSString *address = peer.location; @@ -558,7 +558,7 @@ - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.lastSeenBlock = self.chain.lastSyncBlock.height; - DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); + DSLogPrivate(@"CoinJoin: new block found, restarting masternode connections job"); [self triggerConnections]; } } diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 298393605..595acfcd0 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -1822,12 +1822,10 @@ - (void)acceptGovObjectSyncMessage:(NSData *)message { // MARK: - Accept CoinJoin messages - (void)acceptCoinJoinCompleteMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsc from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_COMPLETE]; } - (void)acceptCoinJoinFinalTransaction:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dsf from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_FINAL_TRANSACTION]; } @@ -1836,12 +1834,10 @@ - (void)acceptCoinJoinQueueMessage:(NSData *)message { } - (void)acceptCoinJoinStatusUpdateMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dssu from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_STATUS_UPDATE]; } - (void)acceptCoinJoinBroadcastTxMessage:(NSData *)message { - DSLog(@"[OBJ-C] CoinJoin: got dstx from %@", self.location); [[DSCoinJoinManager sharedInstanceForChain:self.chain] processMessageFrom:self message:message type:MSG_COINJOIN_BROADCAST_TX]; } diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index d52c101d3..17a4ef48c 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1032,7 +1032,6 @@ - (DSCreditFundingTransaction *)creditFundingTransactionFor:(uint64_t)amount to: return (DSCreditFundingTransaction *)[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 coinControl:nil]; From 09eee81df1a1a89cdcd77dd8965edb1f78b2eb84 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 11 Oct 2024 19:00:19 +0700 Subject: [PATCH 059/133] fix: align derivation path limit gap with Android --- .../Models/Derivation Paths/DSDerivationPath.m | 1 - .../Derivation Paths/DSFundsDerivationPath.h | 1 + .../Derivation Paths/DSFundsDerivationPath.m | 14 ++++++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m index ed8548399..d3a6caae8 100644 --- a/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSDerivationPath.m @@ -358,7 +358,6 @@ - (NSSet *)allAddresses { return [self.mAllAddresses copy]; } - - (NSSet *)usedAddresses { return [self.mUsedAddresses copy]; } diff --git a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h index af8a31869..a3eb354f3 100644 --- a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h +++ b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.h @@ -10,6 +10,7 @@ #define SEQUENCE_GAP_LIMIT_EXTERNAL 10 #define SEQUENCE_GAP_LIMIT_INTERNAL 5 #define SEQUENCE_GAP_LIMIT_INITIAL 100 +#define SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN 400 #define SEQUENCE_UNUSED_GAP_LIMIT_EXTERNAL 10 #define SEQUENCE_UNUSED_GAP_LIMIT_INTERNAL 5 diff --git a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m index 936eefb5f..c3960a44c 100644 --- a/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m +++ b/DashSync/shared/Models/Derivation Paths/DSFundsDerivationPath.m @@ -113,8 +113,18 @@ - (void)loadAddresses { } }]; 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]; + NSUInteger gapLimit = 0; + + if (self.shouldUseReducedGapLimit) { + gapLimit = SEQUENCE_UNUSED_GAP_LIMIT_INITIAL; + } else if (self.type == DSDerivationPathType_AnonymousFunds) { + gapLimit = SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN; + } else { + gapLimit = SEQUENCE_GAP_LIMIT_INITIAL; + } + + [self registerAddressesWithGapLimit:gapLimit internal:YES error:nil]; + [self registerAddressesWithGapLimit:gapLimit internal:NO error:nil]; } } From 5527d76881a78c324e8bb64036ab99e3bf9792a9 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 13 Oct 2024 19:37:42 +0700 Subject: [PATCH 060/133] fix: NPE at DSChainBlockWasLockedNotification and logs --- .../Managers/Chain Managers/DSTransactionManager.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 2082d7d1c..6d9191ef6 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -1749,7 +1749,17 @@ - (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}]; + if (self.chain && block) { + NSDictionary *userInfo = @{ + DSChainManagerNotificationChainKey: self.chain, + DSChainNotificationBlockKey: block + }; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSChainBlockWasLockedNotification object:nil userInfo:userInfo]; + }); + } else { + DSLog(@"Warning: Unable to post notification due to nil chain or block (%s : %s)", self.chain == nil ? "nil" : "valid", block == nil ? "nil" : "valid"); + } }); } else { #if DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS From 1b5af0dbe136f129e590264f5b446b4471b175b1 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 18 Oct 2024 14:45:30 +0700 Subject: [PATCH 061/133] fix: crash & logging fixes --- .../Models/CoinJoin/DSCoinJoinManager.h | 4 +- .../Models/CoinJoin/DSCoinJoinManager.m | 108 ++++++++++-------- .../Models/CoinJoin/DSCoinJoinWrapper.m | 30 ++--- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 40 +++---- Example/Podfile.lock | 6 +- 5 files changed, 98 insertions(+), 90 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index b05ed1bde..0f913e31b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -78,8 +78,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; -- (void)doAutomaticDenominatingWithReport:(BOOL)report; -- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun; +- (void)doAutomaticDenominatingWithDryRun:(BOOL)dryRun completion:(void (^)(BOOL success))completion; - (void)updateSuccessBlock; - (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; @@ -90,7 +89,6 @@ NS_ASSUME_NONNULL_BEGIN - (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; - (void)updateOptionsWithAmount:(uint64_t)amount; - (void)updateOptionsWithEnabled:(BOOL)isEnabled; -- (int32_t)getActiveSessionCount; // Events - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 92c55c8d0..8b455b671 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -34,6 +34,7 @@ #import "DSChain+Protected.h" #import "DSBlock.h" #import "DSKeyManager.h" +#import "DSDerivationPath+Protected.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @@ -46,6 +47,8 @@ @interface DSCoinJoinManager () @property (atomic) int32_t cachedLastSuccessBlock; @property (atomic) int32_t cachedBlockHeight; // Keep track of current block height @property (atomic) double lastReportedProgress; +@property (atomic) BOOL hasReportedSuccess; +@property (atomic) BOOL hasReportedFailure; @end @@ -128,7 +131,7 @@ - (void)updateOptionsWithEnabled:(BOOL)isEnabled { } - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessions:(int32_t)sessions withMultisession:(BOOL)multisession denominationGoal:(int32_t)denomGoal denominationHardCap:(int32_t)denomHardCap { - DSLog(@"CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); + DSLog(@"[%@] CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", self.chain.name, rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); self.options->coinjoin_amount = amount; self.options->coinjoin_rounds = rounds; self.options->coinjoin_sessions = sessions; @@ -162,7 +165,7 @@ - (void)startAsync { } - (void)start { - DSLog(@"CoinJoinManager starting"); + DSLog(@"[%@] CoinJoinManager starting", self.chain.name); [self cancelCoinjoinTimer]; uint32_t interval = 1; uint32_t delay = 1; @@ -212,7 +215,7 @@ - (BOOL)startMixing { - (void)stop { if (self.isMixing) { - DSLog(@"CoinJoinManager stopping"); + DSLog(@"[%@] CoinJoinManager stopping", self.chain.name); self.isMixing = false; [self cancelCoinjoinTimer]; self.cachedLastSuccessBlock = 0; @@ -252,35 +255,48 @@ - (void)handleTransactionReceivedNotification { DSTransaction *lastTransaction = wallet.accounts.firstObject.recentTransactions.firstObject; if ([self.wrapper isMixingFeeTx:lastTransaction.txHash]) { - DSLog(@"CoinJoin tx: Mixing Fee: %@", uint256_reverse_hex(lastTransaction.txHash)); + DSLog(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_MixingFee]; } else if ([self coinJoinTxTypeForTransaction:lastTransaction] == CoinJoinTransactionType_Mixing) { - DSLog(@"CoinJoin tx: Mixing Transaction: %@", uint256_reverse_hex(lastTransaction.txHash)); + DSLog(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; } } -- (void)doAutomaticDenominatingWithReport:(BOOL)report { - if ([self validMNCount] == 0) { +- (void)doAutomaticDenominatingWithDryRun:(BOOL)dryRun completion:(void (^)(BOOL success))completion { + if (![self.wrapper isRegistered]) { + [self.wrapper registerCoinJoin:self.options]; + } + + if (!dryRun && [self validMNCount] == 0) { + NSError *error = [NSError errorWithDomain:@"DSCoinJoinManagerErrorDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: @"No valid masternodes available"}]; + completion(NO); return; - } - + } + + DSLog(@"[%@] CoinJoin: doAutomaticDenominatingWithDryRun: %@", self.chain.name, dryRun ? @"YES" : @"NO"); + dispatch_async(self.processingQueue, ^{ - BOOL result = [self.wrapper doAutomaticDenominatingWithDryRun:NO]; + BOOL result = [self.wrapper doAutomaticDenominatingWithDryRun:dryRun]; - if (report) { - DSLog(@"CoinJoin: Mixing %@", result ? @"started successfully" : @"start failed, will retry"); + if (!dryRun) { + if (result) { + if (!self.hasReportedSuccess) { + DSLog(@"[%@] CoinJoin: Mixing started successfully", self.chain.name); + self.hasReportedSuccess = YES; + self.hasReportedFailure = NO; + } + } else { + if (!self.hasReportedFailure) { + DSLog(@"[%@] CoinJoin: Mixing start failed, will retry", self.chain.name); + self.hasReportedFailure = YES; + self.hasReportedSuccess = NO; + } + } } + + completion(result); }); - } - -- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { - if (![self.wrapper isRegistered]) { - [self.wrapper registerCoinJoin:self.options]; - } - - DSLog(@"CoinJoin: doAutomaticDenominatingWithDryRun: %@", dryRun ? @"YES" : @"NO"); - return [self.wrapper doAutomaticDenominatingWithDryRun:dryRun]; } - (void)cancelCoinjoinTimer { @@ -353,7 +369,11 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { continue; } - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:wtx]; + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + if (!account.coinJoinDerivationPath.addressesLoaded) { + DSLog(@"[%@] CoinJoin selectCoinsGroupedByAddresses: CJDerivationPath addresses NOT loaded", self.chain.name); + } + BOOL isTrusted = wtx.instantSendReceived || [account transactionIsVerified:wtx]; if (skipUnconfirmed && !isTrusted) { @@ -481,7 +501,10 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { for (DSTransaction *coin in spendables) { UInt256 wtxid = coin.txHash; - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:coin]; + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + if (!account.coinJoinDerivationPath.addressesLoaded) { + DSLog(@"[%@] CoinJoin availableCoins: CJDerivationPath addresses NOT loaded", self.chain.name); + } if ([account transactionIsPending:coin]) { continue; @@ -634,7 +657,7 @@ - (double)getMixingProgress { if (self.lastReportedProgress != progress) { _lastReportedProgress = progress; - DSLog(@"CoinJoin: getMixingProgress: %f = %d / (%f * %d)", progress, totalRounds, requiredRounds, totalInputs); + DSLog(@"[%@] CoinJoin: getMixingProgress: %f = %d / (%f * %d)", self.chain.name, progress, totalRounds, requiredRounds, totalInputs); } return fmax(0.0, fmin(progress, 1.0)); @@ -787,14 +810,14 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output BOOL signedTransaction = [account signTransaction:transaction]; if (!signedTransaction || !transaction.isSigned) { - DSLog(@"CoinJoin error: not signed"); + DSLog(@"[%@] CoinJoin error: not signed", self.chain.name); return NO; } else { [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { if (error) { - DSLog(@"CoinJoin publish error: %@ for tx: %@", error.description, transaction.description); + DSLog(@"[%@] CoinJoin publish error: %@ for tx: %@", self.chain.name, error.description, transaction.description); } else { - DSLog(@"CoinJoin publish success: %@", transaction.description); + DSLog(@"[%@] CoinJoin publish success: %@", self.chain.name, transaction.description); } dispatch_async(self.processingQueue, ^{ @@ -838,7 +861,7 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with DSCoinJoinSignedInputs *request = [DSCoinJoinSignedInputs requestWithData:message]; [peer sendRequest:request]; } else { - DSLog(@"CoinJoin: unknown message type: %@", messageType); + DSLog(@"[%@] CoinJoin: unknown message type: %@", self.chain.name, messageType); return NO; } @@ -878,21 +901,6 @@ - (uint64_t)getSmallestDenomination { return [self.wrapper getSmallestDenomination]; } -- (int32_t)getActiveSessionCount { - int32_t result = 0; - NSArray *statuses = [self.wrapper getSessionStatuses]; - - for (NSNumber *status in statuses) { - int statusInt = [status intValue]; - - if (statusInt == PoolStatus_Connecting || statusInt == PoolStatus_Connected || statusInt == PoolStatus_Mixing) { - result += 1; - } - } - - return result; -} - - (DSCoinControl *)selectCoinJoinUTXOs { DSCoinControl *coinControl = [[DSCoinControl alloc] init]; [coinControl useCoinJoin:YES]; @@ -921,29 +929,29 @@ - (void)printUsedKeys { NSArray *issuedAddresses = [self getIssuedReceiveAddresses]; NSArray *usedAddresses = [self getUsedReceiveAddresses]; double percent = (double)usedAddresses.count * 100.0 / (double)issuedAddresses.count; - DSLog(@"CoinJoin init. Used addresses count %d out of %d (%.2f %%)", usedAddresses.count, issuedAddresses.count, percent); + DSLog(@"[%@] CoinJoin init. Used addresses count %lu out of %lu (%.2f %%)", self.chain.name, (unsigned long)usedAddresses.count, (unsigned long)issuedAddresses.count, percent); }); } // Events - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"[%@] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"[%@] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onMixingStarted:(nonnull NSArray *)statuses { - DSLog(@"CoinJoin: onMixingStarted, statuses: %@", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + DSLog(@"[%@] CoinJoin: onMixingStarted, statuses: %@", self.chain.name, statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); [self.managerDelegate mixingStarted]; } - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted { - DSLog(@"CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + DSLog(@"[%@] CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", self.chain.name, isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); BOOL isError = NO; for (NSNumber *statusNumber in statuses) { @@ -952,7 +960,7 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter status != PoolStatus_ErrNotEnoughFunds && status != PoolStatus_ErrNoInputs) { isError = YES; - DSLog(@"CoinJoin: Mixing stopped before completion. Status: %d", status); + DSLog(@"[%@] CoinJoin: Mixing stopped before completion. Status: %d", self.chain.name, status); break; } } @@ -961,7 +969,7 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { - DSLog(@"CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); + DSLog(@"[%@] CoinJoin: onTransactionProcessed: %@, type: %d", self.chain.name, uint256_reverse_hex(txId), type); [self.managerDelegate transactionProcessedWithId:txId type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 23105edff..63a4d5106 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -463,21 +463,23 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinContr DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts coinControl:cc onPublished:^(UInt256 txId, NSError * _Nullable error) { - if (error) { - DSLog(@"CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); - } else if (is_denominating) { - DSLog(@"CoinJoin tx: Denominations Created: %@", uint256_reverse_hex(txId)); - bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); - - if (!isFinished) { - DSLog(@"CoinJoin: auto_denom not finished"); + @synchronized (context) { + if (error) { + DSLog(@"[%@] CoinJoin: commit tx error: %@, tx type: %@", wrapper.chain.name, error, is_denominating ? @"denominations" : @"collateral"); + } else if (is_denominating) { + DSLog(@"[%@] CoinJoin tx: Denominations Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); + + if (!isFinished) { + DSLog(@"[%@] CoinJoin: auto_denom not finished", wrapper.chain.name); + } + + processor_destroy_block_hash(client_session_id); + [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; + } else { + DSLog(@"[%@] CoinJoin tx: Collateral Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } - - processor_destroy_block_hash(client_session_id); - [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; - } else { - DSLog(@"CoinJoin tx: Collateral Created: %@", uint256_reverse_hex(txId)); - [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } }]; } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 65f775401..1640de770 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -116,7 +116,7 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { - DSLog(@"CoinJoin: masternode[closing] %@", [self hostFor:ip]); + DSLog(@"[%@] CoinJoin: masternode[closing] %@", self.chain.name, [self hostFor:ip]); @synchronized (self.mutablePendingClosingMasternodes) { [self.mutablePendingClosingMasternodes addObject:peer]; @@ -144,10 +144,10 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B if (warn) { if (![self isNodePending:ip port:port]) { - DSLog(@"CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); + DSLog(@"[%@] CoinJoin: Cannot find %@ in the list of connected peers: %@", self.chain.name, [self hostFor:ip], listOfPeers); NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); } else { - DSLog(@"CoinJoin: %@ in the list of pending peers", [self hostFor:ip]); + DSLog(@"[%@] CoinJoin: %@ in the list of pending peers", self.chain.name, [self hostFor:ip]); } } @@ -176,7 +176,7 @@ - (NSSet *)pendingPeers { } } -- (NSMutableArray *)pendingClosingMasternodes { +- (NSArray *)pendingClosingMasternodes { @synchronized(self.mutablePendingClosingMasternodes) { return [self.mutablePendingClosingMasternodes copy]; } @@ -213,7 +213,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; if (delay > 0.1) { - DSLog(@"CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); + DSLog(@"[%@] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", self.chain.name, delay, peerToTry == NULL ? @"" : peerToTry.location); [self triggerConnectionsJobWithDelay:delay]; return; } @@ -273,7 +273,7 @@ - (DSPeer *)getNextPendingMasternode { if (peerWithLeastBackoff) { [self.addressMap setObject:sessionValueWithLeastBackoff forKey:peerWithLeastBackoff.location]; - DSLog(@"CoinJoin: discovery: %@ -> %@", peerWithLeastBackoff.location, uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: discovery: %@ -> %@", self.chain.name, peerWithLeastBackoff.location, uint256_hex(sessionId)); } return peerWithLeastBackoff; @@ -296,7 +296,7 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self.pendingSessions) { - DSLog(@"CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", self.chain.name, (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [self.pendingSessions addObject:sessionIdValue]; @@ -314,7 +314,7 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session - (void)updateMaxConnections { _maxConnections = self.pendingSessions.count; NSUInteger connections = MIN(self.maxConnections, self.coinJoinManager.options->coinjoin_sessions); - DSLog(@"CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); + DSLog(@"[%@] CoinJoin: updating max connections to min(%lu, %lu)", self.chain.name, (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); [self updateMaxConnections:connections]; } @@ -362,12 +362,12 @@ - (void)checkMasternodesWithoutSessions { } } else { // TODO(DashJ): we may not need this anymore - DSLog(@"CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: session is not connected to a masternode: %@", self.chain.name, uint256_hex(sessionId)); } } if (!found) { - DSLog(@"CoinJoin: masternode is not connected to a session: %@", peer.location); + DSLog(@"[%@] CoinJoin: masternode is not connected to a session: %@", self.chain.name, peer.location); [masternodesToDrop addObject:peer]; } } @@ -376,7 +376,7 @@ - (void)checkMasternodesWithoutSessions { for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; - DSLog(@"CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + DSLog(@"[%@] CoinJoin: masternode will be disconnected: %@: %@", self.chain.name, peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); [self.mutablePendingClosingMasternodes addObject:peer]; [peer disconnect]; } @@ -395,12 +395,12 @@ - (BOOL)connectTo:(DSPeer *)peer { DSLogPrivate(@"CoinJoin: connectTo: %@", peer.location); if (![self isMasternodeSessionByPeer:peer]) { - DSLog(@"CoinJoin: %@ not a masternode session, exit", peer.location); + DSLog(@"[%@] CoinJoin: %@ not a masternode session, exit", self.chain.name, peer.location); return NO; } if ([self isNodeConnected:peer] || [self isNodePending:peer]) { - DSLog(@"CoinJoin: attempting to connect to the same masternode again: %@", peer.location); + DSLog(@"[%@] CoinJoin: attempting to connect to the same masternode again: %@", self.chain.name, peer.location); return NO; // do not connect to the same peer again } @@ -418,18 +418,18 @@ - (BOOL)connectTo:(DSPeer *)peer { } if (uint256_is_zero(sessionId)) { - DSLog(@"CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap"); + DSLog(@"[%@] CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap", self.chain.name); return NO; } DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; if (!mixingMasternodeAddress) { - DSLog(@"CoinJoin: session is not connected to a masternode, sessionId: %@", uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: session is not connected to a masternode, sessionId: %@", self.chain.name, uint256_hex(sessionId)); return NO; } - DSLog(@"CoinJoin: masternode[connecting] %@: %@; %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: masternode[connecting] %@: %@; %@", self.chain.name, peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.chain.chainManager.governanceSyncManager sporkDelegate:self.chain.chainManager.sporkManager masternodeDelegate:self.chain.chainManager.masternodeManager queue:self.networkingQueue]; peer.earliestKeyTime = self.chain.earliestWalletCreationTime;; @@ -470,7 +470,7 @@ - (void)peerConnected:(nonnull DSPeer *)peer { [self.groupBackoff trackSuccess]; [[self.backoffMap objectForKey:peer.location] trackSuccess]; - DSLog(@"CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); + DSLog(@"[%@] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", self.chain.name, peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers addObject:peer]; @@ -486,7 +486,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; - DSLog(@"CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); + DSLog(@"[%@] CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", self.chain.name, peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; @@ -506,7 +506,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err } } - DSLog(@"CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"[%@] CoinJoin: handling this mn peer death: %@ -> %@", self.chain.name, peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); if (masternode) { NSString *address = peer.location; @@ -558,7 +558,7 @@ - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.lastSeenBlock = self.chain.lastSyncBlock.height; - DSLogPrivate(@"CoinJoin: new block found, restarting masternode connections job"); + DSLogPrivate(@"[%@] CoinJoin: new block found, restarting masternode connections job", self.chain.name); [self triggerConnections]; } } diff --git a/Example/Podfile.lock b/Example/Podfile.lock index d3c85e26e..194684b89 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -586,7 +586,7 @@ PODS: - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - DAPI-GRPC/Messages - gRPC-ProtoRPC - - DashSharedCore (0.4.16) + - DashSharedCore (0.4.17) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) @@ -713,7 +713,7 @@ SPEC CHECKSUMS: CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b + DashSharedCore: ae839c5f91a4e581a27090898f52ac512e0fee0c DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b @@ -727,6 +727,6 @@ SPEC CHECKSUMS: tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 -PODFILE CHECKSUM: faadd211f49d42e97495e9acfb4c5ac2f117c898 +PODFILE CHECKSUM: 4960dc263d8e28c53fd46c9ea6a5aa2f0ee1395d COCOAPODS: 1.15.2 From 17c1a92376298c21e60f826e12b6d261733930d9 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 13 Oct 2024 19:37:42 +0700 Subject: [PATCH 062/133] fix: NPE at DSChainBlockWasLockedNotification and logs --- .../Managers/Chain Managers/DSTransactionManager.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 2082d7d1c..6d9191ef6 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -1749,7 +1749,17 @@ - (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}]; + if (self.chain && block) { + NSDictionary *userInfo = @{ + DSChainManagerNotificationChainKey: self.chain, + DSChainNotificationBlockKey: block + }; + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSChainBlockWasLockedNotification object:nil userInfo:userInfo]; + }); + } else { + DSLog(@"Warning: Unable to post notification due to nil chain or block (%s : %s)", self.chain == nil ? "nil" : "valid", block == nil ? "nil" : "valid"); + } }); } else { #if DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS From 8b8df3a40fcb1f1e304e37abcb0b82d89b0accd1 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 18 Oct 2024 14:45:30 +0700 Subject: [PATCH 063/133] fix: crash & logging fixes --- .../Models/CoinJoin/DSCoinJoinManager.h | 4 +- .../Models/CoinJoin/DSCoinJoinManager.m | 128 ++++++++++-------- .../Models/CoinJoin/DSCoinJoinWrapper.m | 30 ++-- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 40 +++--- Example/Podfile.lock | 6 +- 5 files changed, 112 insertions(+), 96 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index b05ed1bde..0f913e31b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -78,8 +78,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type; - (void)setStopOnNothingToDo:(BOOL)stop; - (BOOL)startMixing; -- (void)doAutomaticDenominatingWithReport:(BOOL)report; -- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun; +- (void)doAutomaticDenominatingWithDryRun:(BOOL)dryRun completion:(void (^)(BOOL success))completion; - (void)updateSuccessBlock; - (void)refreshUnusedKeys; - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; @@ -90,7 +89,6 @@ NS_ASSUME_NONNULL_BEGIN - (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; - (void)updateOptionsWithAmount:(uint64_t)amount; - (void)updateOptionsWithEnabled:(BOOL)isEnabled; -- (int32_t)getActiveSessionCount; // Events - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 91c4a957e..b36facf42 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -34,6 +34,7 @@ #import "DSChain+Protected.h" #import "DSBlock.h" #import "DSKeyManager.h" +#import "DSDerivationPath+Protected.h" int32_t const DEFAULT_MIN_DEPTH = 0; int32_t const DEFAULT_MAX_DEPTH = 9999999; @@ -45,6 +46,9 @@ @interface DSCoinJoinManager () @property (nonatomic, strong) dispatch_source_t coinjoinTimer; @property (atomic) int32_t cachedLastSuccessBlock; @property (atomic) int32_t cachedBlockHeight; // Keep track of current block height +@property (atomic) double lastReportedProgress; +@property (atomic) BOOL hasReportedSuccess; +@property (atomic) BOOL hasReportedFailure; @end @@ -125,7 +129,7 @@ - (void)updateOptionsWithEnabled:(BOOL)isEnabled { } - (void)configureMixingWithAmount:(uint64_t)amount rounds:(int32_t)rounds sessions:(int32_t)sessions withMultisession:(BOOL)multisession denominationGoal:(int32_t)denomGoal denominationHardCap:(int32_t)denomHardCap { - DSLog(@"[OBJ-C] CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); + DSLog(@"[%@] CoinJoin: mixing configuration: { rounds: %d, sessions: %d, amount: %llu, multisession: %s, denomGoal: %d, denomHardCap: %d }", self.chain.name, rounds, sessions, amount, multisession ? "YES" : "NO", denomGoal, denomHardCap); self.options->coinjoin_amount = amount; self.options->coinjoin_rounds = rounds; self.options->coinjoin_sessions = sessions; @@ -166,7 +170,7 @@ - (void)startAsync { } - (void)start { - DSLog(@"[OBJ-C] CoinJoinManager starting, time: %@", [NSDate date]); + DSLog(@"[%@] CoinJoinManager starting", self.chain.name); [self cancelCoinjoinTimer]; uint32_t interval = 1; uint32_t delay = 1; @@ -217,7 +221,7 @@ - (BOOL)startMixing { - (void)stop { if (self.isMixing) { - DSLog(@"[OBJ-C] CoinJoinManager stopping"); + DSLog(@"[%@] CoinJoinManager stopping", self.chain.name); self.isMixing = false; [self cancelCoinjoinTimer]; self.cachedLastSuccessBlock = 0; @@ -258,42 +262,48 @@ - (void)handleTransactionReceivedNotification { DSTransaction *lastTransaction = wallet.accounts.firstObject.recentTransactions.firstObject; if ([self.wrapper isMixingFeeTx:lastTransaction.txHash]) { - DSLog(@"[OBJ-C] CoinJoin tx: Mixing Fee: %@", uint256_reverse_hex(lastTransaction.txHash)); + DSLog(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_MixingFee]; } else if ([self coinJoinTxTypeForTransaction:lastTransaction] == CoinJoinTransactionType_Mixing) { - DSLog(@"[OBJ-C] CoinJoin tx: Mixing Transaction: %@", uint256_reverse_hex(lastTransaction.txHash)); + DSLog(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; } } -- (void)doAutomaticDenominatingWithReport:(BOOL)report { - if ([self validMNCount] == 0) { - DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); +- (void)doAutomaticDenominatingWithDryRun:(BOOL)dryRun completion:(void (^)(BOOL success))completion { + if (![self.wrapper isRegistered]) { + [self.wrapper registerCoinJoin:self.options]; + } + + if (!dryRun && [self validMNCount] == 0) { + NSError *error = [NSError errorWithDomain:@"DSCoinJoinManagerErrorDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: @"No valid masternodes available"}]; + completion(NO); return; - } - + } + + DSLog(@"[%@] CoinJoin: doAutomaticDenominatingWithDryRun: %@", self.chain.name, dryRun ? @"YES" : @"NO"); + dispatch_async(self.processingQueue, ^{ - DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); - BOOL result = [self.wrapper doAutomaticDenominatingWithDryRun:NO]; + BOOL result = [self.wrapper doAutomaticDenominatingWithDryRun:dryRun]; - if (report) { - DSLog(@"[OBJ-C] CoinJoin: Mixing %@", result ? @"started successfully" : @"start failed, will retry"); + if (!dryRun) { + if (result) { + if (!self.hasReportedSuccess) { + DSLog(@"[%@] CoinJoin: Mixing started successfully", self.chain.name); + self.hasReportedSuccess = YES; + self.hasReportedFailure = NO; + } + } else { + if (!self.hasReportedFailure) { + DSLog(@"[%@] CoinJoin: Mixing start failed, will retry", self.chain.name); + self.hasReportedFailure = YES; + self.hasReportedSuccess = NO; + } + } } + + completion(result); }); - } - -- (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { - if ([self validMNCount] == 0) { - DSLog(@"[OBJ-C] CoinJoin doAutomaticDenominating: No Masternodes detected."); - return false; - } - - if (![self.wrapper isRegistered]) { - [self.wrapper registerCoinJoin:self.options]; - } - - DSLog(@"[OBJ-C] CoinJoin: doAutomaticDenominating, time: %@", [NSDate date]); - return [self.wrapper doAutomaticDenominatingWithDryRun:dryRun]; } - (void)cancelCoinjoinTimer { @@ -366,7 +376,11 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { continue; } - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:wtx]; + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + if (!account.coinJoinDerivationPath.addressesLoaded) { + DSLog(@"[%@] CoinJoin selectCoinsGroupedByAddresses: CJDerivationPath addresses NOT loaded", self.chain.name); + } + BOOL isTrusted = wtx.instantSendReceived || [account transactionIsVerified:wtx]; if (skipUnconfirmed && !isTrusted) { @@ -494,7 +508,10 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { for (DSTransaction *coin in spendables) { UInt256 wtxid = coin.txHash; - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:coin]; + DSAccount *account = self.chain.wallets.firstObject.accounts.firstObject; + if (!account.coinJoinDerivationPath.addressesLoaded) { + DSLog(@"[%@] CoinJoin availableCoins: CJDerivationPath addresses NOT loaded", self.chain.name); + } if ([account transactionIsPending:coin]) { continue; @@ -644,7 +661,12 @@ - (double)getMixingProgress { }]; double progress = totalInputs != 0 ? (double)totalRounds / (requiredRounds * totalInputs) : 0.0; - DSLog(@"[OBJ-C] CoinJoin: getMixingProgress: %f = %d / (%f * %d)", progress, totalRounds, requiredRounds, totalInputs); + + if (self.lastReportedProgress != progress) { + _lastReportedProgress = progress; + DSLog(@"[%@] CoinJoin: getMixingProgress: %f = %d / (%f * %d)", self.chain.name, progress, totalRounds, requiredRounds, totalInputs); + } + return fmax(0.0, fmin(progress, 1.0)); } @@ -795,14 +817,14 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output BOOL signedTransaction = [account signTransaction:transaction]; if (!signedTransaction || !transaction.isSigned) { - DSLog(@"[OBJ-C] CoinJoin error: not signed"); + DSLog(@"[%@] CoinJoin error: not signed", self.chain.name); return NO; } else { [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { if (error) { - DSLog(@"[OBJ-C] CoinJoin publish error: %@ for tx: %@", error.description, transaction.description); + DSLog(@"[%@] CoinJoin publish error: %@ for tx: %@", self.chain.name, error.description, transaction.description); } else { - DSLog(@"[OBJ-C] CoinJoin publish success: %@", transaction.description); + DSLog(@"[%@] CoinJoin publish success: %@", self.chain.name, transaction.description); } dispatch_async(self.processingQueue, ^{ @@ -846,7 +868,7 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with DSCoinJoinSignedInputs *request = [DSCoinJoinSignedInputs requestWithData:message]; [peer sendRequest:request]; } else { - DSLog(@"[OBJ-C] CoinJoin: unknown message type: %@", messageType); + DSLog(@"[%@] CoinJoin: unknown message type: %@", self.chain.name, messageType); return NO; } @@ -886,21 +908,6 @@ - (uint64_t)getSmallestDenomination { return [self.wrapper getSmallestDenomination]; } -- (int32_t)getActiveSessionCount { - int32_t result = 0; - NSArray *statuses = [self.wrapper getSessionStatuses]; - - for (NSNumber *status in statuses) { - int statusInt = [status intValue]; - - if (statusInt == PoolStatus_Connecting || statusInt == PoolStatus_Connected || statusInt == PoolStatus_Mixing) { - result += 1; - } - } - - return result; -} - - (DSCoinControl *)selectCoinJoinUTXOs { DSCoinControl *coinControl = [[DSCoinControl alloc] init]; [coinControl useCoinJoin:YES]; @@ -924,25 +931,34 @@ - (DSCoinControl *)selectCoinJoinUTXOs { return coinControl; } +- (void)printUsedKeys { + dispatch_async(self.processingQueue, ^{ + NSArray *issuedAddresses = [self getIssuedReceiveAddresses]; + NSArray *usedAddresses = [self getUsedReceiveAddresses]; + double percent = (double)usedAddresses.count * 100.0 / (double)issuedAddresses.count; + DSLog(@"[%@] CoinJoin init. Used addresses count %lu out of %lu (%.2f %%)", self.chain.name, (unsigned long)usedAddresses.count, (unsigned long)issuedAddresses.count, percent); + }); +} + // Events - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"[OBJ-C] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"[%@] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"[OBJ-C] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"[%@] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; } - (void)onMixingStarted:(nonnull NSArray *)statuses { - DSLog(@"[OBJ-C] CoinJoin: onMixingStarted, statuses: %@", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + DSLog(@"[%@] CoinJoin: onMixingStarted, statuses: %@", self.chain.name, statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); [self.managerDelegate mixingStarted]; } - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted { - DSLog(@"[OBJ-C] CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + DSLog(@"[%@] CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", self.chain.name, isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); BOOL isError = NO; for (NSNumber *statusNumber in statuses) { @@ -951,7 +967,7 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter status != PoolStatus_ErrNotEnoughFunds && status != PoolStatus_ErrNoInputs) { isError = YES; - DSLog(@"[OBJ-C] CoinJoin: Mixing stopped before completion. Status: %d", status); + DSLog(@"[%@] CoinJoin: Mixing stopped before completion. Status: %d", self.chain.name, status); break; } } @@ -960,7 +976,7 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { - DSLog(@"[OBJ-C] CoinJoin: onTransactionProcessed: %@, type: %d", uint256_reverse_hex(txId), type); + DSLog(@"[%@] CoinJoin: onTransactionProcessed: %@, type: %d", self.chain.name, uint256_reverse_hex(txId), type); [self.managerDelegate transactionProcessedWithId:txId type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index b806b69a2..8af6ff546 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -467,21 +467,23 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinContr DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts coinControl:cc onPublished:^(UInt256 txId, NSError * _Nullable error) { - if (error) { - DSLog(@"[OBJ-C] CoinJoin: commit tx error: %@, tx type: %@", error, is_denominating ? @"denominations" : @"collateral"); - } else if (is_denominating) { - DSLog(@"[OBJ-C] CoinJoin tx: Denominations Created: %@", uint256_reverse_hex(txId)); - bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); - - if (!isFinished) { - DSLog(@"[OBJ-C] CoinJoin: auto_denom not finished"); + @synchronized (context) { + if (error) { + DSLog(@"[%@] CoinJoin: commit tx error: %@, tx type: %@", wrapper.chain.name, error, is_denominating ? @"denominations" : @"collateral"); + } else if (is_denominating) { + DSLog(@"[%@] CoinJoin tx: Denominations Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); + + if (!isFinished) { + DSLog(@"[%@] CoinJoin: auto_denom not finished", wrapper.chain.name); + } + + processor_destroy_block_hash(client_session_id); + [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; + } else { + DSLog(@"[%@] CoinJoin tx: Collateral Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } - - processor_destroy_block_hash(client_session_id); - [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; - } else { - DSLog(@"[OBJ-C] CoinJoin tx: Collateral Created: %@", uint256_reverse_hex(txId)); - [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } }]; } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 4cb0d12fc..9515d56fa 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -116,7 +116,7 @@ - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { return [self forPeer:ip port:port warn:YES withPredicate:^BOOL(DSPeer *peer) { - DSLog(@"[OBJ-C] CoinJoin: masternode[closing] %@", [self hostFor:ip]); + DSLog(@"[%@] CoinJoin: masternode[closing] %@", self.chain.name, [self hostFor:ip]); @synchronized (self.mutablePendingClosingMasternodes) { [self.mutablePendingClosingMasternodes addObject:peer]; @@ -144,10 +144,10 @@ - (BOOL)forPeer:(UInt128)ip port:(uint16_t)port warn:(BOOL)warn withPredicate:(B if (warn) { if (![self isNodePending:ip port:port]) { - DSLog(@"[OBJ-C] CoinJoin: Cannot find %@ in the list of connected peers: %@", [self hostFor:ip], listOfPeers); + DSLog(@"[%@] CoinJoin: Cannot find %@ in the list of connected peers: %@", self.chain.name, [self hostFor:ip], listOfPeers); NSAssert(NO, @"Cannot find %@", [self hostFor:ip]); } else { - DSLog(@"[OBJ-C] CoinJoin: %@ in the list of pending peers", [self hostFor:ip]); + DSLog(@"[%@] CoinJoin: %@ in the list of pending peers", self.chain.name, [self hostFor:ip]); } } @@ -176,7 +176,7 @@ - (NSSet *)pendingPeers { } } -- (NSMutableArray *)pendingClosingMasternodes { +- (NSArray *)pendingClosingMasternodes { @synchronized(self.mutablePendingClosingMasternodes) { return [self.mutablePendingClosingMasternodes copy]; } @@ -213,7 +213,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; if (delay > 0.1) { - DSLog(@"[OBJ-C] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", delay, peerToTry == NULL ? @"" : peerToTry.location); + DSLog(@"[%@] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", self.chain.name, delay, peerToTry == NULL ? @"" : peerToTry.location); [self triggerConnectionsJobWithDelay:delay]; return; } @@ -273,7 +273,7 @@ - (DSPeer *)getNextPendingMasternode { if (peerWithLeastBackoff) { [self.addressMap setObject:sessionValueWithLeastBackoff forKey:peerWithLeastBackoff.location]; - DSLog(@"[OBJ-C] CoinJoin: discovery: %@ -> %@", peerWithLeastBackoff.location, uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: discovery: %@ -> %@", self.chain.name, peerWithLeastBackoff.location, uint256_hex(sessionId)); } return peerWithLeastBackoff; @@ -296,7 +296,7 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { @synchronized (self.pendingSessions) { - DSLog(@"[OBJ-C] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", self.chain.name, (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; [self.pendingSessions addObject:sessionIdValue]; @@ -314,7 +314,7 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session - (void)updateMaxConnections { _maxConnections = self.pendingSessions.count; NSUInteger connections = MIN(self.maxConnections, self.coinJoinManager.options->coinjoin_sessions); - DSLog(@"[OBJ-C] CoinJoin: updating max connections to min(%lu, %lu)", (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); + DSLog(@"[%@] CoinJoin: updating max connections to min(%lu, %lu)", self.chain.name, (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); [self updateMaxConnections:connections]; } @@ -362,12 +362,12 @@ - (void)checkMasternodesWithoutSessions { } } else { // TODO(DashJ): we may not need this anymore - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode: %@", uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: session is not connected to a masternode: %@", self.chain.name, uint256_hex(sessionId)); } } if (!found) { - DSLog(@"[OBJ-C] CoinJoin: masternode is not connected to a session: %@", peer.location); + DSLog(@"[%@] CoinJoin: masternode is not connected to a session: %@", self.chain.name, peer.location); [masternodesToDrop addObject:peer]; } } @@ -376,7 +376,7 @@ - (void)checkMasternodesWithoutSessions { for (DSPeer *peer in masternodesToDrop) { DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; - DSLog(@"[OBJ-C] CoinJoin: masternode will be disconnected: %@: %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); + DSLog(@"[%@] CoinJoin: masternode will be disconnected: %@: %@", self.chain.name, peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); [self.mutablePendingClosingMasternodes addObject:peer]; [peer disconnect]; } @@ -395,12 +395,12 @@ - (BOOL)connectTo:(DSPeer *)peer { DSLog(@"[OBJ-C] CoinJoin: connectTo: %@", peer.location); if (![self isMasternodeSessionByPeer:peer]) { - DSLog(@"[OBJ-C] CoinJoin: %@ not a masternode session, exit", peer.location); + DSLog(@"[%@] CoinJoin: %@ not a masternode session, exit", self.chain.name, peer.location); return NO; } if ([self isNodeConnected:peer] || [self isNodePending:peer]) { - DSLog(@"[OBJ-C] CoinJoin: attempting to connect to the same masternode again: %@", peer.location); + DSLog(@"[%@] CoinJoin: attempting to connect to the same masternode again: %@", self.chain.name, peer.location); return NO; // do not connect to the same peer again } @@ -418,18 +418,18 @@ - (BOOL)connectTo:(DSPeer *)peer { } if (uint256_is_zero(sessionId)) { - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap"); + DSLog(@"[%@] CoinJoin: session is not connected to a masternode, proTxHashKey not found in masternodeMap", self.chain.name); return NO; } DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; if (!mixingMasternodeAddress) { - DSLog(@"[OBJ-C] CoinJoin: session is not connected to a masternode, sessionId: %@", uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: session is not connected to a masternode, sessionId: %@", self.chain.name, uint256_hex(sessionId)); return NO; } - DSLog(@"[OBJ-C] CoinJoin: masternode[connecting] %@: %@; %@", peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); + DSLog(@"[%@] CoinJoin: masternode[connecting] %@: %@; %@", self.chain.name, peer.location, uint256_hex(mn.providerRegistrationTransactionHash), uint256_hex(sessionId)); [peer setChainDelegate:self.chain.chainManager peerDelegate:self transactionDelegate:self.chain.chainManager.transactionManager governanceDelegate:self.chain.chainManager.governanceSyncManager sporkDelegate:self.chain.chainManager.sporkManager masternodeDelegate:self.chain.chainManager.masternodeManager queue:self.networkingQueue]; peer.earliestKeyTime = self.chain.earliestWalletCreationTime;; @@ -470,7 +470,7 @@ - (void)peerConnected:(nonnull DSPeer *)peer { [self.groupBackoff trackSuccess]; [[self.backoffMap objectForKey:peer.location] trackSuccess]; - DSLog(@"[OBJ-C] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); + DSLog(@"[%@] CoinJoin: New peer %@ ({%lu connected, %lu pending, %lu max)", self.chain.name, peer.location, self.mutableConnectedPeers.count, self.mutablePendingPeers.count, self.maxConnections); [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers addObject:peer]; @@ -486,7 +486,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; - DSLog(@"[OBJ-C] CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); + DSLog(@"[%@] CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", self.chain.name, peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; @@ -506,7 +506,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err } } - DSLog(@"[OBJ-C] CoinJoin: handling this mn peer death: %@ -> %@", peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"[%@] CoinJoin: handling this mn peer death: %@ -> %@", self.chain.name, peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); if (masternode) { NSString *address = peer.location; @@ -558,7 +558,7 @@ - (DSPeer *)peerForLocation:(UInt128)ipAddress port:(uint16_t)port { - (void)handleSyncStateDidChangeNotification:(NSNotification *)note { if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]] && self.chain.lastSyncBlock.height > self.lastSeenBlock) { self.lastSeenBlock = self.chain.lastSyncBlock.height; - DSLog(@"[OBJ-C] CoinJoin: new block found, restarting masternode connections job"); + DSLogPrivate(@"[%@] CoinJoin: new block found, restarting masternode connections job", self.chain.name); [self triggerConnections]; } } diff --git a/Example/Podfile.lock b/Example/Podfile.lock index d3c85e26e..194684b89 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -586,7 +586,7 @@ PODS: - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - DAPI-GRPC/Messages - gRPC-ProtoRPC - - DashSharedCore (0.4.16) + - DashSharedCore (0.4.17) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) @@ -713,7 +713,7 @@ SPEC CHECKSUMS: CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b + DashSharedCore: ae839c5f91a4e581a27090898f52ac512e0fee0c DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b @@ -727,6 +727,6 @@ SPEC CHECKSUMS: tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 -PODFILE CHECKSUM: faadd211f49d42e97495e9acfb4c5ac2f117c898 +PODFILE CHECKSUM: 4960dc263d8e28c53fd46c9ea6a5aa2f0ee1395d COCOAPODS: 1.15.2 From fad1b1a0cddc84eab1b0b6d3530523cc25122856 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 24 Oct 2024 17:28:19 +0700 Subject: [PATCH 064/133] chore: cleanup --- DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m | 1 - 1 file changed, 1 deletion(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 8b455b671..75ca59e7c 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -269,7 +269,6 @@ - (void)doAutomaticDenominatingWithDryRun:(BOOL)dryRun completion:(void (^)(BOOL } if (!dryRun && [self validMNCount] == 0) { - NSError *error = [NSError errorWithDomain:@"DSCoinJoinManagerErrorDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: @"No valid masternodes available"}]; completion(NO); return; } From 618d98103aa2461adb60b09edfc2cb630ea22202 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 24 Oct 2024 18:41:14 +0700 Subject: [PATCH 065/133] fix: report new blocks even if not started --- .../Models/CoinJoin/DSCoinJoinManager.m | 27 ++++++------------- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 3 --- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 75ca59e7c..2ae3e6ee1 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -150,15 +150,6 @@ - (BOOL)isChainSynced { - (void)startAsync { if (!self.masternodeGroup.isRunning) { - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleSyncStateDidChangeNotification:) - name:DSChainManagerSyncStateDidChangeNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleTransactionReceivedNotification) - name:DSTransactionManagerTransactionReceivedNotification - object:nil]; - [self.chain.chainManager.peerManager shouldSendDsq:true]; [self.masternodeGroup startAsync]; } @@ -169,7 +160,10 @@ - (void)start { [self cancelCoinjoinTimer]; uint32_t interval = 1; uint32_t delay = 1; - + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleTransactionReceivedNotification) + name:DSTransactionManagerTransactionReceivedNotification + object:nil]; @synchronized (self) { self.cachedBlockHeight = self.chain.lastSyncBlock.height; self.options->enable_coinjoin = YES; @@ -192,15 +186,6 @@ - (void)start { } - (void)doMaintenance { - // TODO: - // report masternode group - // if (masternodeGroup != null) { - // tick++; - // if (tick % 15 == 0) { - // log.info(masternodeGroup.toString()); - // } - // } - if ([self validMNCount] == 0) { return; } @@ -210,6 +195,10 @@ - (void)doMaintenance { - (BOOL)startMixing { self.isMixing = true; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleSyncStateDidChangeNotification:) + name:DSChainManagerSyncStateDidChangeNotification + object:nil]; return [self.wrapper startMixing]; } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 1640de770..b3b89ce73 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -485,9 +485,6 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err @synchronized (self.peersLock) { [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; - - DSLog(@"[%@] CoinJoin: %@ died with error %@: (%lu connected, %lu pending, %lu max)", self.chain.name, peer.location, error, (unsigned long)self.mutableConnectedPeers.count, (unsigned long)self.mutablePendingPeers.count, (unsigned long)self.maxConnections); - [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; From 4d36bc2e22f91fd6d94611f4a8b62670aae454ec Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 24 Oct 2024 18:42:14 +0700 Subject: [PATCH 066/133] fix: example build --- Example/DashSync/DSCoinJoinViewController.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Example/DashSync/DSCoinJoinViewController.m b/Example/DashSync/DSCoinJoinViewController.m index 9ae6e5f21..ebd77347b 100644 --- a/Example/DashSync/DSCoinJoinViewController.m +++ b/Example/DashSync/DSCoinJoinViewController.m @@ -55,7 +55,9 @@ - (void)startCoinJoin { DSLog(@"[OBJ-C] CoinJoin: Mixing has been started already."); } - [self.coinJoinManager doAutomaticDenominatingWithDryRun:NO]; + [self.coinJoinManager doAutomaticDenominatingWithDryRun:NO completion:^(BOOL success) { + + }]; } @end From 14de3abc945386a4940cfa20106f9f914b87f8fe Mon Sep 17 00:00:00 2001 From: pankcuf Date: Fri, 25 Oct 2024 18:49:37 +0700 Subject: [PATCH 067/133] fix: incorrect polymorphism --- .../DSBlockchainIdentityCloseTransition.m | 4 ++-- .../DSBlockchainIdentityUpdateTransition.m | 4 ++-- DashSync/shared/Models/Transactions/Base/DSTransaction.h | 2 +- DashSync/shared/Models/Transactions/Base/DSTransaction.m | 7 ++----- .../Models/Transactions/Coinbase/DSCoinbaseTransaction.m | 5 +++-- .../Provider/DSProviderRegistrationTransaction.m | 5 +++-- .../Provider/DSProviderUpdateRegistrarTransaction.m | 5 +++-- .../Provider/DSProviderUpdateRevocationTransaction.m | 5 +++-- .../Provider/DSProviderUpdateServiceTransaction.m | 5 +++-- .../Transactions/Quorums/DSQuorumCommitmentTransaction.m | 5 +++-- 10 files changed, 25 insertions(+), 22 deletions(-) diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m index 63612fedb..10fb8a1ee 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityCloseTransition.m @@ -87,9 +87,9 @@ @implementation DSBlockchainIdentityCloseTransition // return data; //} // -//- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex +//- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay //{ -// NSMutableData * data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; +// NSMutableData * data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; // NSData * payloadData = [self payloadData]; // [data appendVarInt:payloadData.length]; // [data appendData:payloadData]; diff --git a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m index 4701614c4..5dd4ccdbc 100644 --- a/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m +++ b/DashSync/shared/Models/Platform/Transitions/BlockchainIdentity/DSBlockchainIdentityUpdateTransition.m @@ -165,9 +165,9 @@ @implementation DSBlockchainIdentityUpdateTransition //} // // -//- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex +//- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay //{ -// NSMutableData * data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; +// NSMutableData * data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; // NSData * payloadData = [self payloadData]; // [data appendVarInt:payloadData.length]; // [data appendData:payloadData]; diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.h b/DashSync/shared/Models/Transactions/Base/DSTransaction.h index 5f808a845..e5a0dee4a 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.h +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.h @@ -155,7 +155,7 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) // priority = sum(input_amount_in_satoshis*input_age_in_blocks)/tx_size_in_bytes - (uint64_t)priorityForAmounts:(NSArray *)amounts withAges:(NSArray *)ages; -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex; +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay; - (BOOL)hasNonDustOutputInWallet:(DSWallet *)wallet; diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index 814b396b5..54822c671 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -419,10 +419,6 @@ - (NSData *)toData:(BOOL)anyoneCanPay { // Returns the binary transaction data that needs to be hashed and signed with the private key for the tx input at // subscriptIndex. A subscriptIndex of NSNotFound will return the entire signed transaction. -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { - return [self toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:NO]; -} - - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { NSArray *inputs = self.inputs; @@ -655,11 +651,12 @@ - (BOOL)signWithPrivateKeys:(NSArray *)keys anyoneCanPay:(BOOL)anyoneCanPay { } - (BOOL)signWithPreorderedPrivateKeys:(NSArray *)keys { + // TODO: Function isn't used at all except commented out `testIdentityGrindingAttack` @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]; + NSData *data = [self toDataWithSubscriptIndex:i anyoneCanPay:NO]; UInt256 hash = data.SHA256_2; NSValue *keyValue = keys[i]; OpaqueKey *key = ((OpaqueKey *) keyValue.pointerValue); diff --git a/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m b/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m index 3c2370ba6..fd17a7fb9 100644 --- a/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m +++ b/DashSync/shared/Models/Transactions/Coinbase/DSCoinbaseTransaction.m @@ -103,9 +103,10 @@ - (NSData *)payloadData { // Returns the binary transaction data that needs to be hashed and signed with the private key for the tx input at // subscriptIndex. A subscriptIndex of NSNotFound will return the entire signed transaction. -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex + anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; return [data appendCountedData:[self payloadData]]; } } diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m index b49269d17..4a9c3526a 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderRegistrationTransaction.m @@ -229,9 +229,10 @@ - (NSData *)payloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex + anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m index 07a340f71..cd7210ae8 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRegistrarTransaction.m @@ -180,9 +180,10 @@ - (NSData *)payloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex + anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; NSData *payloadData = [self payloadData]; [data appendVarInt:payloadData.length]; [data appendData:payloadData]; diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m index 7de1b9d2c..c062365b6 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateRevocationTransaction.m @@ -148,9 +148,10 @@ - (NSData *)payloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex + anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; diff --git a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m index cbd2ee993..1fe7cb4aa 100644 --- a/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m +++ b/DashSync/shared/Models/Transactions/Provider/DSProviderUpdateServiceTransaction.m @@ -197,9 +197,10 @@ - (NSData *)payloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex + anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; diff --git a/DashSync/shared/Models/Transactions/Quorums/DSQuorumCommitmentTransaction.m b/DashSync/shared/Models/Transactions/Quorums/DSQuorumCommitmentTransaction.m index 7b4325e65..500e09d95 100644 --- a/DashSync/shared/Models/Transactions/Quorums/DSQuorumCommitmentTransaction.m +++ b/DashSync/shared/Models/Transactions/Quorums/DSQuorumCommitmentTransaction.m @@ -132,9 +132,10 @@ - (NSData *)payloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex + anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; From e8143c3d5b9b918ccc437405effe92b902fd8c19 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sat, 26 Oct 2024 14:14:01 +0700 Subject: [PATCH 068/133] fix: synchronization fixes --- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 110 ++++++++++-------- 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index b3b89ce73..b4ad45c81 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -51,7 +51,6 @@ @interface DSMasternodeGroup () @property (nullable, nonatomic, readwrite) DSPeer *downloadPeer; @property (nonatomic, strong) DSBackoff *groupBackoff; @property (nonatomic, strong) NSMutableDictionary *backoffMap; -@property (nonatomic, strong) NSLock *lock; @property (nonatomic) uint32_t lastSeenBlock; @end @@ -234,50 +233,51 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } - (DSPeer *)getNextPendingMasternode { - @synchronized(self.addressMap) { - NSArray *pendingSessionsCopy = [self.pendingSessions copy]; - DSPeer *peerWithLeastBackoff = nil; - NSValue *sessionValueWithLeastBackoff = nil; - UInt256 sessionId = UINT256_ZERO; - NSDate *leastBackoffTime = [NSDate distantFuture]; + NSArray *pendingSessionsCopy = [self.pendingSessions copy]; + NSArray *pendingClosingMasternodesCopy = self.pendingClosingMasternodes; + DSPeer *peerWithLeastBackoff = nil; + NSValue *sessionValueWithLeastBackoff = nil; + UInt256 sessionId = UINT256_ZERO; + NSDate *leastBackoffTime = [NSDate distantFuture]; - for (NSValue *sessionValue in pendingSessionsCopy) { - [sessionValue getValue:&sessionId]; - DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; + for (NSValue *sessionValue in pendingSessionsCopy) { + [sessionValue getValue:&sessionId]; + DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; - if (mixingMasternodeInfo) { - UInt128 ipAddress = mixingMasternodeInfo.address; - uint16_t port = mixingMasternodeInfo.port; - DSPeer *peer = [self peerForLocation:ipAddress port:port]; + if (mixingMasternodeInfo) { + UInt128 ipAddress = mixingMasternodeInfo.address; + uint16_t port = mixingMasternodeInfo.port; + DSPeer *peer = [self peerForLocation:ipAddress port:port]; - if (peer == nil) { - peer = [DSPeer peerWithAddress:ipAddress andPort:port onChain:self.chain]; - } + if (peer == nil) { + peer = [DSPeer peerWithAddress:ipAddress andPort:port onChain:self.chain]; + } - if (![self.mutablePendingClosingMasternodes containsObject:peer] && ![self isNodeConnected:peer] && ![self isNodePending:peer]) { - DSBackoff *backoff = [self.backoffMap objectForKey:peer.location]; + if (![pendingClosingMasternodesCopy containsObject:peer] && ![self isNodeConnected:peer] && ![self isNodePending:peer]) { + DSBackoff *backoff = [self.backoffMap objectForKey:peer.location]; - if (!backoff) { - backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:BACKOFF_MULTIPLIER]; - [self.backoffMap setObject:backoff forKey:peer.location]; - } + if (!backoff) { + backoff = [[DSBackoff alloc] initInitialBackoff:DEFAULT_INITIAL_BACKOFF maxBackoff:DEFAULT_MAX_BACKOFF multiplier:BACKOFF_MULTIPLIER]; + [self.backoffMap setObject:backoff forKey:peer.location]; + } - if ([backoff.retryTime compare:leastBackoffTime] == NSOrderedAscending) { - leastBackoffTime = backoff.retryTime; - peerWithLeastBackoff = peer; - sessionValueWithLeastBackoff = sessionValue; - } + if ([backoff.retryTime compare:leastBackoffTime] == NSOrderedAscending) { + leastBackoffTime = backoff.retryTime; + peerWithLeastBackoff = peer; + sessionValueWithLeastBackoff = sessionValue; } } } + } - if (peerWithLeastBackoff) { + if (peerWithLeastBackoff) { + @synchronized(self.addressMap) { [self.addressMap setObject:sessionValueWithLeastBackoff forKey:peerWithLeastBackoff.location]; DSLog(@"[%@] CoinJoin: discovery: %@ -> %@", self.chain.name, peerWithLeastBackoff.location, uint256_hex(sessionId)); } - - return peerWithLeastBackoff; } + + return peerWithLeastBackoff; } - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { @@ -375,9 +375,13 @@ - (void)checkMasternodesWithoutSessions { DSLogPrivate(@"CoinJoin: need to drop %lu masternodes", (unsigned long)masternodesToDrop.count); for (DSPeer *peer in masternodesToDrop) { - DSSimplifiedMasternodeEntry *mn = [_chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; + DSSimplifiedMasternodeEntry *mn = [self.chain.chainManager.masternodeManager masternodeAtLocation:peer.address port:peer.port]; DSLog(@"[%@] CoinJoin: masternode will be disconnected: %@: %@", self.chain.name, peer.location, uint256_hex(mn.providerRegistrationTransactionHash)); - [self.mutablePendingClosingMasternodes addObject:peer]; + + @synchronized (self.mutablePendingClosingMasternodes) { + [self.mutablePendingClosingMasternodes addObject:peer]; + } + [peer disconnect]; } } @@ -493,32 +497,36 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err [self triggerConnections]; } } - - @synchronized (self.pendingSessions) { - DSPeer *masternode = NULL; + + DSPeer *masternode = NULL; + NSArray *pendingClosingMasternodes = self.pendingClosingMasternodes; - for (DSPeer *mn in self.pendingClosingMasternodes) { - if ([peer.location isEqualToString:mn.location]) { - masternode = mn; - } + for (DSPeer *mn in pendingClosingMasternodes) { + if ([peer.location isEqualToString:mn.location]) { + masternode = mn; } + } - DSLog(@"[%@] CoinJoin: handling this mn peer death: %@ -> %@", self.chain.name, peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); + DSLog(@"[%@] CoinJoin: handling this mn peer death: %@ -> %@", self.chain.name, peer.location, masternode != NULL ? masternode.location : @"not found in closing list"); - if (masternode) { - NSString *address = peer.location; + if (masternode) { + NSString *address = peer.location; - if ([self.mutablePendingClosingMasternodes containsObject:masternode]) { - // if this is part of pendingClosingMasternodes, where we want to close the connection, - // we don't want to increase the backoff time - [[self.backoffMap objectForKey:address] trackSuccess]; - } + if ([pendingClosingMasternodes containsObject:masternode]) { + // if this is part of pendingClosingMasternodes, where we want to close the connection, + // we don't want to increase the backoff time + [[self.backoffMap objectForKey:address] trackSuccess]; + } + @synchronized (self.mutablePendingClosingMasternodes) { [self.mutablePendingClosingMasternodes removeObject:masternode]; - UInt256 proTxHash = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; - NSValue *proTxHashKey = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; - NSValue *sessionIdObject = [self.masternodeMap objectForKey:proTxHashKey]; + } + + UInt256 proTxHash = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; + NSValue *proTxHashKey = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; + NSValue *sessionIdObject = [self.masternodeMap objectForKey:proTxHashKey]; + @synchronized (self.pendingSessions) { if (sessionIdObject) { [self.pendingSessions removeObject:sessionIdObject]; [self.sessionMap removeObjectForKey:sessionIdObject]; From 668ca4c6f536fdb906b7e2c01f24161356f20745 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 30 Oct 2024 20:19:43 +0700 Subject: [PATCH 069/133] fix: private logs & memory management --- .../Models/CoinJoin/DSCoinJoinManager.m | 25 ++++++++++++++++--- .../Models/CoinJoin/DSCoinJoinWrapper.m | 24 ++++++++++-------- .../Models/CoinJoin/DSCompactTallyItem.m | 2 +- .../Models/CoinJoin/DSTransaction+CoinJoin.m | 4 +-- .../CoinJoin/DSTransactionOutput+CoinJoin.m | 8 +++--- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 5 ++-- DashSync/shared/Models/Wallet/DSAccount.m | 8 ++++-- Example/Podfile.lock | 4 +-- 8 files changed, 52 insertions(+), 28 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 2ae3e6ee1..c37605c4c 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -244,10 +244,18 @@ - (void)handleTransactionReceivedNotification { DSTransaction *lastTransaction = wallet.accounts.firstObject.recentTransactions.firstObject; if ([self.wrapper isMixingFeeTx:lastTransaction.txHash]) { - DSLog(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); + #if DEBUG + DSLogPrivate(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); + #else + DSLog(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, @""); + #endif [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_MixingFee]; } else if ([self coinJoinTxTypeForTransaction:lastTransaction] == CoinJoinTransactionType_Mixing) { - DSLog(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); + #if DEBUG + DSLogPrivate(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); + #else + DSLog(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, @""); + #endif [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; } } @@ -802,10 +810,15 @@ - (BOOL)commitTransactionForAmounts:(NSArray *)amounts outputs:(NSArray *)output return NO; } else { [self.chain.chainManager.transactionManager publishTransaction:transaction completion:^(NSError *error) { + NSString *txDescription = @""; + #if DEBUG + txDescription = transaction.description; + #endif + if (error) { - DSLog(@"[%@] CoinJoin publish error: %@ for tx: %@", self.chain.name, error.description, transaction.description); + DSLog(@"[%@] CoinJoin publish error: %@ for tx: %@", self.chain.name, error.description, txDescription); } else { - DSLog(@"[%@] CoinJoin publish success: %@", self.chain.name, transaction.description); + DSLog(@"[%@] CoinJoin publish success: %@", self.chain.name, txDescription); } dispatch_async(self.processingQueue, ^{ @@ -957,7 +970,11 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { +#if DEBUG DSLog(@"[%@] CoinJoin: onTransactionProcessed: %@, type: %d", self.chain.name, uint256_reverse_hex(txId), type); +#else + DSLog(@"[%@] CoinJoin: onTransactionProcessed: %@, type: %d", self.chain.name, @"", type); +#endif [self.managerDelegate transactionProcessedWithId:txId type:type]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 63a4d5106..11eb488e8 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -283,7 +283,6 @@ - (void)dealloc { } } - processor_destroy_block_hash(prevout_hash); return inputValue; } @@ -296,7 +295,6 @@ bool hasChainLock(Block *block, const void *context) { hasChainLock = [wrapper.chain blockHeightChainLocked:block->height]; } - processor_destroy_block(block); return hasChainLock; } @@ -313,7 +311,6 @@ bool hasChainLock(Block *block, const void *context) { } } - processor_destroy_block_hash(tx_hash); return tx; } @@ -325,7 +322,6 @@ bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { result = [AS_OBJC(context).manager isMineInput:txHash index:index]; } - processor_destroy_block_hash(tx_hash); return result; } @@ -388,7 +384,7 @@ void destroySelectedCoins(SelectedCoins *selectedCoins) { return; } - if (selectedCoins->item_count > 0 && selectedCoins->items) { + if (selectedCoins->items) { for (int i = 0; i < selectedCoins->item_count; i++) { [DSCompactTallyItem ffi_free:selectedCoins->items[i]]; } @@ -404,10 +400,9 @@ void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { return; } - if (gatheredOutputs->item_count > 0 && gatheredOutputs->items) { + if (gatheredOutputs->items) { for (int i = 0; i < gatheredOutputs->item_count; i++) { - [DSTransactionOutput ffi_free:gatheredOutputs->items[i]->output]; - free(gatheredOutputs->items[i]->outpoint_hash); + [DSInputCoin ffi_free:gatheredOutputs->items[i]]; } free(gatheredOutputs->items); @@ -420,7 +415,6 @@ void destroyGatheredOutputs(GatheredOutputs *gatheredOutputs) { @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); DSTransaction *tx = [[DSTransaction alloc] initWithTransaction:transaction onChain:wrapper.chain]; - destroy_transaction(transaction); BOOL isSigned = [wrapper.chain.wallets.firstObject.accounts.firstObject signTransaction:tx anyoneCanPay:anyoneCanPay]; if (isSigned) { @@ -467,7 +461,11 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinContr if (error) { DSLog(@"[%@] CoinJoin: commit tx error: %@, tx type: %@", wrapper.chain.name, error, is_denominating ? @"denominations" : @"collateral"); } else if (is_denominating) { - DSLog(@"[%@] CoinJoin tx: Denominations Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + #if DEBUG + DSLog(@"[%@] CoinJoin tx: Denominations Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + #else + DSLog(@"[%@] CoinJoin tx: Denominations Created: %@", wrapper.chain.name, @""); + #endif bool isFinished = finish_automatic_denominating(wrapper.clientManager, client_session_id); if (!isFinished) { @@ -477,7 +475,11 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinContr processor_destroy_block_hash(client_session_id); [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_CreateDenomination]; } else { - DSLog(@"[%@] CoinJoin tx: Collateral Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + #if DEBUG + DSLog(@"[%@] CoinJoin tx: Collateral Created: %@", wrapper.chain.name, uint256_reverse_hex(txId)); + #else + DSLog(@"[%@] CoinJoin tx: Collateral Created: %@", wrapper.chain.name, @""); + #endif [wrapper.manager onTransactionProcessed:txId type:CoinJoinTransactionType_MakeCollateralInputs]; } } diff --git a/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m b/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m index fb8b8f28a..59573e557 100644 --- a/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m +++ b/DashSync/shared/Models/CoinJoin/DSCompactTallyItem.m @@ -55,7 +55,7 @@ + (void)ffi_free:(CompactTallyItem *)item { free(item->tx_destination); - if (item->input_coins_size > 0 && item->input_coins) { + if (item->input_coins) { for (int i = 0; i < item->input_coins_size; i++) { [DSInputCoin ffi_free:item->input_coins[i]]; } diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m index fdad29ec1..4cb3ae06c 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -111,7 +111,7 @@ + (void)ffi_free:(Transaction *)tx { free(tx->tx_hash); - if (tx->inputs_count > 0 && tx->inputs) { + if (tx->inputs) { for (int i = 0; i < tx->inputs_count; i++) { [DSTransactionInput ffi_free:tx->inputs[i]]; } @@ -119,7 +119,7 @@ + (void)ffi_free:(Transaction *)tx { free(tx->inputs); } - if (tx->outputs_count > 0 && tx->outputs) { + if (tx->outputs) { for (int i = 0; i < tx->outputs_count; i++) { [DSTransactionOutput ffi_free:tx->outputs[i]]; } diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m index 2f74c0377..9dc1f284c 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m @@ -32,13 +32,14 @@ - (TransactionOutput *)ffi_malloc:(ChainType)type { transactionOutput->script = data_malloc(scriptData); char *c_string = address_with_script_pubkey(self.outScript.bytes, self.outScript.length, type); - + if (c_string) { size_t addressLength = strlen(c_string); transactionOutput->address_length = (uintptr_t)addressLength; transactionOutput->address = (uint8_t *)c_string; } else { transactionOutput->address_length = 0; + transactionOutput->address = NULL; } return transactionOutput; @@ -51,9 +52,8 @@ + (void)ffi_free:(TransactionOutput *)output { free(output->script); } - if (output->address && output->address_length > 0) { - // TODO: should we use processor_destroy_string(c_string) here? - free(output->address); + if (output->address) { + processor_destroy_string(output->address); } free(output); diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index b4ad45c81..721f0c7ac 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -69,6 +69,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { _addressMap = [NSMutableDictionary dictionary]; _mutableConnectedPeers = [NSMutableSet set]; _mutablePendingPeers = [NSMutableSet set]; + _peersLock = [[NSObject alloc] init]; _downloadPeer = nil; _maxConnections = 0; _shouldSendDsq = true; @@ -197,7 +198,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } @synchronized (self.groupBackoff) { - NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; + NSUInteger numPeers = self.pendingPeers.count + self.connectedPeers.count; if (numPeers >= self.maxConnections) { return; @@ -224,7 +225,7 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } } - NSUInteger count = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; + NSUInteger count = self.pendingPeers.count + self.connectedPeers.count; if (count < self.maxConnections) { [self triggerConnectionsJobWithDelay:0]; // Try next peer immediately. diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 17a4ef48c..73fbfc85b 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -350,14 +350,18 @@ - (NSString *)changeAddress { // returns the first unused coinjoin address - (NSString *)coinJoinReceiveAddress { NSString *address = self.coinJoinDerivationPath.receiveAddress; - [self.coinJoinDerivationPath registerTransactionAddress:address]; // TODO: recheck if needed + dispatch_sync(self.wallet.chain.networkingQueue, ^{ + [self.coinJoinDerivationPath registerTransactionAddress:address]; + }); return address; } // returns the first unused coinjoin address - (NSString *)coinJoinChangeAddress { NSString *address = self.coinJoinDerivationPath.changeAddress; - [self.coinJoinDerivationPath registerTransactionAddress:address]; // TODO: recheck if needed + dispatch_sync(self.wallet.chain.networkingQueue, ^{ + [self.coinJoinDerivationPath registerTransactionAddress:address]; + }); return address; } diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 194684b89..ca75e4b73 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -656,7 +656,7 @@ PODS: - gRPC/Interface-Legacy (1.49.0): - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) - - Protobuf (3.27.2) + - Protobuf (3.28.2) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -722,7 +722,7 @@ SPEC CHECKSUMS: gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2 + Protobuf: 28c89b24435762f60244e691544ed80f50d82701 SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 From 02f184c2e50c98b14b9afa7cd5a54f898e6f35d6 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 15 Nov 2024 15:21:19 +0700 Subject: [PATCH 070/133] fix: improve error mode detection --- .../Models/CoinJoin/DSCoinJoinManager.h | 15 ++-- .../Models/CoinJoin/DSCoinJoinManager.m | 89 +++++++++++++------ .../Models/CoinJoin/DSCoinJoinWrapper.h | 2 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 33 ++++--- .../CoinJoin/DSTransactionOutput+CoinJoin.m | 2 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 68 +++++++------- .../Base/DSAssetUnlockTransaction.m | 2 +- Example/Podfile.lock | 8 +- 8 files changed, 137 insertions(+), 82 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index 0f913e31b..ec5811450 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -29,10 +29,10 @@ NS_ASSUME_NONNULL_BEGIN @protocol DSCoinJoinManagerDelegate -- (void)sessionStartedWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; -- (void)sessionCompleteWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)sessionStartedWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)sessionCompleteWithId:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)mixingStarted; -- (void)mixingComplete:(BOOL)withError isInterrupted:(BOOL)isInterrupted; +- (void)mixingComplete:(BOOL)withError errorStatus:(PoolStatus)errorStatus isInterrupted:(BOOL)isInterrupted; - (void)transactionProcessedWithId:(UInt256)txId type:(CoinJoinTransactionType)type; @end @@ -84,15 +84,16 @@ NS_ASSUME_NONNULL_BEGIN - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; - (double)getMixingProgress; - (DSCoinControl *)selectCoinJoinUTXOs; - - (uint64_t)getSmallestDenomination; -- (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; +- (void)hasCollateralInputsWithOnlyConfirmed:(BOOL)onlyConfirmed completion:(void (^)(BOOL balance))completion; +- (void)calculateAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed completion:(void (^)(uint64_t balance))completion; +- (void)minimumAnonymizableBalanceWithCompletion:(void (^)(uint64_t balance))completion; - (void)updateOptionsWithAmount:(uint64_t)amount; - (void)updateOptionsWithEnabled:(BOOL)isEnabled; // Events -- (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; -- (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined; +- (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined; - (void)onMixingStarted:(nonnull NSArray *)statuses; - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted; - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index c37605c4c..937bbdf55 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -244,18 +244,18 @@ - (void)handleTransactionReceivedNotification { DSTransaction *lastTransaction = wallet.accounts.firstObject.recentTransactions.firstObject; if ([self.wrapper isMixingFeeTx:lastTransaction.txHash]) { - #if DEBUG +#if DEBUG DSLogPrivate(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); - #else +#else DSLog(@"[%@] CoinJoin tx: Mixing Fee: %@", self.chain.name, @""); - #endif +#endif [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_MixingFee]; } else if ([self coinJoinTxTypeForTransaction:lastTransaction] == CoinJoinTransactionType_Mixing) { - #if DEBUG +#if DEBUG DSLogPrivate(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, uint256_reverse_hex(lastTransaction.txHash)); - #else +#else DSLog(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, @""); - #endif +#endif [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; } } @@ -394,8 +394,12 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { if (maxOupointsPerAddress != -1 && tallyItem != nil && tallyItem.inputCoins.count >= maxOupointsPerAddress) { continue; } - - if ([account isSpent:dsutxo_obj(((DSUTXO){outpoint.hash, i}))] || is_locked_coin(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { + + if ([account isSpent:dsutxo_obj(((DSUTXO){outpoint.hash, i}))]) { + continue; + } + + if (is_locked_coin(walletEx, (uint8_t (*)[32])(outpoint.hash.u8), (uint32_t)i)) { continue; } @@ -432,7 +436,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { [tallyItem.inputCoins addObject:coin]; } } - + // construct resulting vector // NOTE: vecTallyRet is "sorted" by txdest (i.e. address), just like mapTally NSMutableArray *vecTallyRet = [NSMutableArray array]; @@ -445,7 +449,7 @@ - (BOOL)isMineInput:(UInt256)txHash index:(uint32_t)index { [vecTallyRet addObject:item]; } - + // Note: cache is assigned in dash-shared-core return vecTallyRet; @@ -487,7 +491,7 @@ - (uint32_t)countInputsWithAmount:(uint64_t)inputAmount { @synchronized(self) { CoinType coinType = coinControl != nil ? coinControl.coinType : CoinType_AllCoins; - + uint64_t total = 0; // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses BOOL allowUsedAddresses = /* !IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE) || */ (coinControl != nil && !coinControl.avoidAddressReuse); @@ -843,11 +847,11 @@ - (DSMasternodeList *)mnList { } - (BOOL)isMasternodeOrDisconnectRequested:(UInt128)ip port:(uint16_t)port { - return [_masternodeGroup isMasternodeOrDisconnectRequested:ip port:port]; + return [self.masternodeGroup isMasternodeOrDisconnectRequested:ip port:port]; } - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { - return [_masternodeGroup disconnectMasternode:ip port:port]; + return [self.masternodeGroup disconnectMasternode:ip port:port]; } - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message withPeerIP:(UInt128)address port:(uint16_t)port warn:(BOOL)warn { @@ -871,7 +875,7 @@ - (BOOL)sendMessageOfType:(NSString *)messageType message:(NSData *)message with } - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { - return [_masternodeGroup addPendingMasternode:proTxHash clientSessionId:sessionId]; + return [self.masternodeGroup addPendingMasternode:proTxHash clientSessionId:sessionId]; } - (void)updateSuccessBlock { @@ -894,8 +898,24 @@ - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transac return [self.wrapper coinJoinTxTypeForTransaction:transaction]; } -- (uint64_t)getAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed { - return [self.wrapper getAnonymizableBalance:skipDenominated skipUnconfirmed:skipUnconfirmed]; +- (void)calculateAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed completion:(void (^)(uint64_t balance))completion { + dispatch_async(self.processingQueue, ^{ + uint64_t balance = [self.wrapper getAnonymizableBalance:skipDenominated skipUnconfirmed:skipUnconfirmed]; + completion(balance); + }); +} + +- (void)minimumAnonymizableBalanceWithCompletion:(void (^)(uint64_t balance))completion { + dispatch_async(self.processingQueue, ^{ + uint64_t valueMin = [self.wrapper getSmallestDenomination]; + BOOL hasCollateralInputs = [self.wrapper hasCollateralInputs:YES]; + + if (hasCollateralInputs) { + valueMin += [self.wrapper getMaxCollateralAmount]; + } + + completion(valueMin); + }); } - (uint64_t)getSmallestDenomination { @@ -936,14 +956,14 @@ - (void)printUsedKeys { // Events -- (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { +- (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined { DSLog(@"[%@] CoinJoin: onSessionStarted: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); - [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; + [self.managerDelegate sessionStartedWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message poolStatus:status ipAddress:address isJoined:joined]; } -- (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message ipAddress:(UInt128)address isJoined:(BOOL)joined { +- (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined { DSLog(@"[%@] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); - [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message ipAddress:address isJoined:joined]; + [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message poolStatus:status ipAddress:address isJoined:joined]; } - (void)onMixingStarted:(nonnull NSArray *)statuses { @@ -952,21 +972,30 @@ - (void)onMixingStarted:(nonnull NSArray *)statuses { } - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted { - DSLog(@"[%@] CoinJoin: onMixingComplete, isInterrupted: %@, statuses: %@", self.chain.name, isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); + uint32_t locked_coins_amount = has_locked_coins(self.wrapper.clientManager); + DSLog(@"[%@] CoinJoin: onMixingComplete, locked_coins_amount: %lu, isInterrupted: %@, statuses: %@", self.chain.name, locked_coins_amount, isInterrupted ? @"YES" : @"NO", statuses.count > 0 ? [NSString stringWithFormat:@"%@", statuses] : @"empty"); - BOOL isError = NO; + PoolStatus returnStatus = PoolStatus_ErrNotEnoughFunds; + BOOL isError = YES; + for (NSNumber *statusNumber in statuses) { PoolStatus status = [statusNumber intValue]; - if (status != PoolStatus_Finished && - status != PoolStatus_ErrNotEnoughFunds && - status != PoolStatus_ErrNoInputs) { - isError = YES; - DSLog(@"[%@] CoinJoin: Mixing stopped before completion. Status: %d", self.chain.name, status); + if (![self isError:status]) { + returnStatus = status; + isError = NO; break; } + + if (status != PoolStatus_ErrNotEnoughFunds) { + returnStatus = status; + } + } + + if (locked_coins_amount > 0) { + print_locked_coins(self.wrapper.clientManager); } - [self.managerDelegate mixingComplete:isError isInterrupted:isInterrupted]; + [self.managerDelegate mixingComplete:isError errorStatus:returnStatus isInterrupted:isInterrupted]; } - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type { @@ -978,4 +1007,8 @@ - (void)onTransactionProcessed:(UInt256)txId type:(CoinJoinTransactionType)type [self.managerDelegate transactionProcessedWithId:txId type:type]; } +- (BOOL)isError:(PoolStatus)status { + return (status & 0x2000) != 0; +} + @end diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 5cb2b1e47..50a1b2324 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -50,6 +50,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateOptions:(CoinJoinClientOptions *)options; - (NSArray *)getStandardDenominations; - (uint64_t)getCollateralAmount; +- (uint64_t)getMaxCollateralAmount; +- (BOOL)hasCollateralInputs:(BOOL)onlyConfirmed; - (uint32_t)amountToDenomination:(uint64_t)amount; - (int32_t)getRealOutpointCoinJoinRounds:(DSUTXO)utxo; - (BOOL)isLockedCoin:(DSUTXO)utxo; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 11eb488e8..5d137badd 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -204,6 +204,18 @@ - (uint64_t)getCollateralAmount { } } +- (uint64_t)getMaxCollateralAmount { + @synchronized (self) { + return get_max_collateral_amount(); + } +} + +- (BOOL)hasCollateralInputs:(BOOL)onlyConfirmed { + @synchronized (self) { + return has_collateral_inputs(_clientManager, onlyConfirmed); + } +} + - (uint32_t)amountToDenomination:(uint64_t)amount { @synchronized (self) { return amount_to_denomination(amount); @@ -590,22 +602,23 @@ bool isWaitingForNewBlock(const void *context) { } void sessionLifecycleListener(bool is_complete, - int32_t base_session_id, - uint8_t (*client_session_id)[32], - uint32_t denomination, - enum PoolState state, - enum PoolMessage message, - uint8_t (*ip_address)[16], - bool joined, - const void *context) { + int32_t base_session_id, + uint8_t (*client_session_id)[32], + uint32_t denomination, + enum PoolState state, + enum PoolMessage message, + enum PoolStatus status, + uint8_t (*ip_address)[16], + bool joined, + const void *context) { @synchronized (context) { UInt256 clientSessionId = *((UInt256 *)client_session_id); UInt128 ipAddress = *((UInt128 *)ip_address); if (is_complete) { - [AS_OBJC(context).manager onSessionComplete:base_session_id clientSessionId:clientSessionId denomination:denomination poolState:state poolMessage:message ipAddress:ipAddress isJoined:joined]; + [AS_OBJC(context).manager onSessionComplete:base_session_id clientSessionId:clientSessionId denomination:denomination poolState:state poolMessage:message poolStatus:status ipAddress:ipAddress isJoined:joined]; } else { - [AS_OBJC(context).manager onSessionStarted:base_session_id clientSessionId:clientSessionId denomination:denomination poolState:state poolMessage:message ipAddress:ipAddress isJoined:joined]; + [AS_OBJC(context).manager onSessionStarted:base_session_id clientSessionId:clientSessionId denomination:denomination poolState:state poolMessage:message poolStatus:status ipAddress:ipAddress isJoined:joined]; } } } diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m index 9dc1f284c..76aa55d91 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m @@ -53,7 +53,7 @@ + (void)ffi_free:(TransactionOutput *)output { } if (output->address) { - processor_destroy_string(output->address); +// processor_destroy_string(output->address); TODO } free(output); diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index 721f0c7ac..c84054c5e 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -120,10 +120,9 @@ - (BOOL)disconnectMasternode:(UInt128)ip port:(uint16_t)port { @synchronized (self.mutablePendingClosingMasternodes) { [self.mutablePendingClosingMasternodes addObject:peer]; - // TODO (dashj): what if this disconnects the wrong one - [self updateMaxConnections]; } + [self updateMaxConnections]; [peer disconnect]; return true; @@ -197,36 +196,41 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { return; } - @synchronized (self.groupBackoff) { - NSUInteger numPeers = self.pendingPeers.count + self.connectedPeers.count; + NSUInteger numPeers = self.pendingPeers.count + self.connectedPeers.count; - if (numPeers >= self.maxConnections) { - return; - } + if (numPeers >= self.maxConnections) { + return; + } - NSDate *now = [NSDate date]; - DSPeer *peerToTry = [self getNextPendingMasternode]; + NSDate *now = [NSDate date]; + DSPeer *peerToTry = [self getNextPendingMasternode]; - if (peerToTry) { - NSDate *retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; - retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; - NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; + if (peerToTry) { + NSDate *retryTime = [self.backoffMap objectForKey:peerToTry.location].retryTime; + retryTime = [retryTime laterDate:self.groupBackoff.retryTime]; + NSTimeInterval delay = [retryTime timeIntervalSinceDate:now]; - if (delay > 0.1) { - DSLog(@"[%@] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", self.chain.name, delay, peerToTry == NULL ? @"" : peerToTry.location); - [self triggerConnectionsJobWithDelay:delay]; - return; - } + if (delay > 0.1) { + DSLog(@"[%@] CoinJoin: Waiting %fl s before next connect attempt to masternode to %@", self.chain.name, delay, peerToTry == NULL ? @"" : peerToTry.location); + [self triggerConnectionsJobWithDelay:delay]; + return; + } - [self connectTo:peerToTry]; + [self connectTo:peerToTry]; + } + + NSUInteger count = self.maxConnections; + + @synchronized (self.peersLock) { + if (peerToTry) { [self.groupBackoff trackSuccess]; } else { [self.groupBackoff trackFailure]; } + + count = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; } - NSUInteger count = self.pendingPeers.count + self.connectedPeers.count; - if (count < self.maxConnections) { [self triggerConnectionsJobWithDelay:0]; // Try next peer immediately. } @@ -304,11 +308,11 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; [self.masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; [self.sessionMap setObject:proTxHashKey forKey:sessionIdValue]; - - [self checkMasternodesWithoutSessions]; - [self updateMaxConnections]; } + [self checkMasternodesWithoutSessions]; + [self updateMaxConnections]; + return true; } @@ -397,7 +401,7 @@ - (NSString *)hostFor:(UInt128)address { } - (BOOL)connectTo:(DSPeer *)peer { - DSLogPrivate(@"CoinJoin: connectTo: %@", peer.location); + DSLogPrivate(@"[%@] CoinJoin: connectTo: %@", self.chain.name, peer.location); if (![self isMasternodeSessionByPeer:peer]) { DSLog(@"[%@] CoinJoin: %@ not a masternode session, exit", self.chain.name, peer.location); @@ -487,16 +491,18 @@ - (void)peerConnected:(nonnull DSPeer *)peer { } - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)error { + NSUInteger numPeers = self.maxConnections; + @synchronized (self.peersLock) { [self.mutablePendingPeers removeObject:peer]; [self.mutableConnectedPeers removeObject:peer]; [self.groupBackoff trackFailure]; [[self.backoffMap objectForKey:peer.location] trackFailure]; - NSUInteger numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; - - if (numPeers < self.maxConnections) { - [self triggerConnections]; - } + numPeers = self.mutablePendingPeers.count + self.mutableConnectedPeers.count; + } + + if (numPeers < self.maxConnections) { + [self triggerConnections]; } DSPeer *masternode = NULL; @@ -522,7 +528,7 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err @synchronized (self.mutablePendingClosingMasternodes) { [self.mutablePendingClosingMasternodes removeObject:masternode]; } - + UInt256 proTxHash = [self.chain.chainManager.masternodeManager masternodeAtLocation:masternode.address port:masternode.port].providerRegistrationTransactionHash; NSValue *proTxHashKey = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; NSValue *sessionIdObject = [self.masternodeMap objectForKey:proTxHashKey]; diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m b/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m index 01824db34..c6b77e254 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m @@ -82,7 +82,7 @@ - (NSData *)basePayloadData { - (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:NO] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 57060f467..ffbfc1900 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -586,7 +586,7 @@ PODS: - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - DAPI-GRPC/Messages - gRPC-ProtoRPC - - DashSharedCore (0.4.17) + - DashSharedCore (0.4.19) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) @@ -656,7 +656,7 @@ PODS: - gRPC/Interface-Legacy (1.49.0): - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) - - Protobuf (3.28.2) + - Protobuf (3.28.3) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -713,7 +713,7 @@ SPEC CHECKSUMS: CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: ae839c5f91a4e581a27090898f52ac512e0fee0c + DashSharedCore: 009f29640756017406ee2b0ab5f1073f1856f85e DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b @@ -722,7 +722,7 @@ SPEC CHECKSUMS: gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: 28c89b24435762f60244e691544ed80f50d82701 + Protobuf: 5a8a7781d8e1004302f108977ac2d5b99323146f SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 From 895a56bfe24b759b14b3b01ea1ff486ac63fce28 Mon Sep 17 00:00:00 2001 From: "Vladimir P." Date: Mon, 18 Nov 2024 15:17:51 +0700 Subject: [PATCH 071/133] Revert "feat: migrations for Platform v0.22 to v1" --- DashSync.podspec | 1 + .../Models/DAPI/DSPlatformDocumentsRequest.m | 8 +- .../Networking/DSDAPIPlatformNetworkService.m | 87 +-- .../DSDAPIPlatformNetworkServiceProtocol.h | 26 +- .../Models/Identity/DSBlockchainIdentity.m | 2 +- .../Chain Managers/DSMasternodeManager.m | 6 +- .../Masternode/DSMasternodeListDiffService.m | 2 +- .../Masternode/DSMasternodeListService.h | 6 +- .../Masternode/DSMasternodeListService.m | 4 +- DashSync/shared/dpns-contract.json | 97 +-- Example/DashSync.xcodeproj/project.pbxproj | 10 + Example/Podfile.lock | 673 +++++++++++++++++- Gemfile | 2 +- 13 files changed, 781 insertions(+), 143 deletions(-) diff --git a/DashSync.podspec b/DashSync.podspec index b185dc97d..f052a94ad 100644 --- a/DashSync.podspec +++ b/DashSync.podspec @@ -38,6 +38,7 @@ 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"' diff --git a/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m b/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m index 5019d61fc..1fde02804 100644 --- a/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m +++ b/DashSync/shared/Models/DAPI/DSPlatformDocumentsRequest.m @@ -57,7 +57,7 @@ + (instancetype)dpnsRequestForUsername:(NSString *)username inDomain:(NSString * + (instancetype)dpnsRequestForUserId:(NSData *)userId { DSPlatformDocumentsRequest *platformDocumentsRequest = [[DSPlatformDocumentsRequest alloc] init]; - platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"records.identity == %@", userId]; // why not path predicate and then predicate? + platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"records.dashUniqueIdentityId == %@", userId]; platformDocumentsRequest.startAt = nil; platformDocumentsRequest.limit = 100; platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverIndex; @@ -115,7 +115,7 @@ + (instancetype)dashpayRequestForContactRequestsForSendingUserId:(NSData *)userI platformDocumentsRequest.startAtIncluded = false; platformDocumentsRequest.limit = 100; platformDocumentsRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"$createdAt" ascending:YES]]; - platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; // why not over index? + platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; platformDocumentsRequest.type = DSPlatformDocumentType_Document; platformDocumentsRequest.tableName = @"contactRequest"; platformDocumentsRequest.prove = DSPROVE_PLATFORM_SINDEXES; @@ -131,7 +131,7 @@ + (instancetype)dashpayRequestForContactRequestsForRecipientUserId:(NSData *)use platformDocumentsRequest.startAtIncluded = false; platformDocumentsRequest.limit = 100; platformDocumentsRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"$createdAt" ascending:YES]]; - platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; // why not over index? + platformDocumentsRequest.queryType = DSPlatformQueryType_RangeOverValue; platformDocumentsRequest.type = DSPlatformDocumentType_Document; platformDocumentsRequest.tableName = @"contactRequest"; platformDocumentsRequest.prove = DSPROVE_PLATFORM_SINDEXES; @@ -140,7 +140,7 @@ + (instancetype)dashpayRequestForContactRequestsForRecipientUserId:(NSData *)use + (instancetype)dashpayRequestForContactRequestForSendingUserId:(NSData *)userId toRecipientUserId:(NSData *)toUserId { DSPlatformDocumentsRequest *platformDocumentsRequest = [[DSPlatformDocumentsRequest alloc] init]; - platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"%K == %@ && toUserId == %@", @"$ownerId", userId, toUserId]; // why not path predicate and predicate? + platformDocumentsRequest.pathPredicate = [NSPredicate predicateWithFormat:@"%K == %@ && toUserId == %@", @"$ownerId", userId, toUserId]; platformDocumentsRequest.startAt = nil; platformDocumentsRequest.limit = 100; platformDocumentsRequest.type = DSPlatformDocumentType_Document; diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m index da679a3e6..eb061c47b 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkService.m @@ -450,6 +450,27 @@ - (void)loadBloomFilter:(NSString *)filter #pragma mark Layer 2 +- (id)fetchIdentityIdsByKeyHashes:(NSArray *)keyHashesArray + completionQueue:(dispatch_queue_t)completionQueue + success:(void (^)(NSArray *identityIds))success + failure:(void (^)(NSError *error))failure { + NSParameterAssert(keyHashesArray); + NSParameterAssert(completionQueue); + DSPlatformRequestLog(@"fetchIdentityIdsByKeyHashes %@", keyHashesArray); + GetIdentityIdsByPublicKeyHashesRequest *getIdentityIdsByPublicKeyHashesRequest = [[GetIdentityIdsByPublicKeyHashesRequest alloc] init]; + getIdentityIdsByPublicKeyHashesRequest.publicKeyHashesArray = [keyHashesArray mutableCopy]; + getIdentityIdsByPublicKeyHashesRequest.prove = DSPROVE_PLATFORM; + DSDAPIGRPCResponseHandler *responseHandler = [[DSDAPIGRPCResponseHandler alloc] initForGetIdentityIDsByPublicKeyHashesRequest:keyHashesArray 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 = success; + responseHandler.errorHandler = failure; + GRPCUnaryProtoCall *call = [self.gRPCClient getIdentityIdsByPublicKeyHashesWithMessage:getIdentityIdsByPublicKeyHashesRequest responseHandler:responseHandler callOptions:nil]; + [call start]; + return (id)call; +} + - (id)fetchIdentitiesByKeyHashes:(NSArray *)keyHashesArray completionQueue:(dispatch_queue_t)completionQueue success:(void (^)(NSArray *identityDictionaries))success @@ -457,60 +478,18 @@ - (void)loadBloomFilter:(NSString *)filter NSParameterAssert(keyHashesArray); 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]; - } - - return nil; + GetIdentitiesByPublicKeyHashesRequest *getIdentitiesByPublicKeyHashesRequest = [[GetIdentitiesByPublicKeyHashesRequest alloc] init]; + getIdentitiesByPublicKeyHashesRequest.publicKeyHashesArray = [keyHashesArray mutableCopy]; + getIdentitiesByPublicKeyHashesRequest.prove = DSPROVE_PLATFORM; + DSDAPIGRPCResponseHandler *responseHandler = [[DSDAPIGRPCResponseHandler alloc] initForGetIdentitiesByPublicKeyHashesRequest:keyHashesArray 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 = success; + responseHandler.errorHandler = failure; + GRPCUnaryProtoCall *call = [self.gRPCClient getIdentitiesByPublicKeyHashesWithMessage:getIdentitiesByPublicKeyHashesRequest responseHandler:responseHandler callOptions:nil]; + [call start]; + return (id)call; } - (id)fetchContractForId:(NSData *)contractId diff --git a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h index ad67d2e08..ae5dc9118 100644 --- a/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h +++ b/DashSync/shared/Models/DAPI/Networking/DSDAPIPlatformNetworkServiceProtocol.h @@ -487,21 +487,31 @@ Get a list of users after matching search criteria failure:(void (^)(NSError *error))failure; /** -Get a list of identities corresponding to the provided key hashes. +Get a list of identities knowing only keys they possess -This method makes individual requests for each key hash in the array and returns a list of identity dictionaries upon successful completion. If any request fails, the failure block is called. - -@param keyHashesArray An array of hashes of keys for which to fetch identities. -@param completionQueue The queue on which to execute the success or failure block. -@param success A block object to be executed when all requests finish successfully, providing an array of identity dictionaries. -@param failure A block object to be executed if any request fails, providing the error encountered. -@return Returns an object conforming to `DSDAPINetworkServiceRequest`, or `nil` if no request is made. +@param keyHashesArray An array of hashes of keys +@param completionQueue The queue in which to return the result on +@param success A block object to be executed when the request operation finishes successfully +@param failure A block object to be executed when the request operation finishes unsuccessfully */ - (id)fetchIdentitiesByKeyHashes:(NSArray *)keyHashesArray completionQueue:(dispatch_queue_t)completionQueue success:(void (^)(NSArray *identityDictionaries))success failure:(void (^)(NSError *error))failure; +/** +Get a list of identity Ids knowing only keys they possess + +@param keyHashesArray An array of hashes of keys +@param completionQueue The queue in which to return the result on +@param success A block object to be executed when the request operation finishes successfully +@param failure A block object to be executed when the request operation finishes unsuccessfully +*/ +- (id)fetchIdentityIdsByKeyHashes:(NSArray *)keyHashesArray + completionQueue:(dispatch_queue_t)completionQueue + success:(void (^)(NSArray *identityIds))success + failure:(void (^)(NSError *error))failure; + @end NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Identity/DSBlockchainIdentity.m b/DashSync/shared/Models/Identity/DSBlockchainIdentity.m index bfc7b862f..f62ffd288 100644 --- a/DashSync/shared/Models/Identity/DSBlockchainIdentity.m +++ b/DashSync/shared/Models/Identity/DSBlockchainIdentity.m @@ -2197,7 +2197,7 @@ - (NSString *)dashpayDomainName { @"normalizedLabel": [username lowercaseString], @"normalizedParentDomainName": domain, @"preorderSalt": [self.usernameSalts objectForKey:usernameFullPath], - @"records": @{@"identity": uint256_data(self.uniqueID)}, + @"records": @{@"dashUniqueIdentityId": uint256_data(self.uniqueID)}, @"subdomainRules": @{@"allowSubdomains": @NO} }; DPDocument *document = [self.dpnsDocumentFactory documentOnTable:@"domain" withDataDictionary:dataDictionary usingEntropy:entropyData error:error]; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m index d63a89b47..92cf5bd40 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m @@ -104,15 +104,15 @@ - (instancetype)initWithChain:(DSChain *)chain { #pragma mark - DSMasternodeListServiceDelegate -- (DSMasternodeList *__nullable)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { +- (DSMasternodeList *__nullable)masternodeListSerivceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { return [self processRequestFromFileForBlockHash:blockHash]; } -- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { +- (void)masternodeListSerivceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash { [self removeOutdatedMasternodeListsBeforeBlockHash:blockHash]; } -- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service { +- (void)masternodeListSerivceEmptiedRetrievalQueue:(DSMasternodeListService *)service { if (![self.masternodeListDiffService retrievalQueueCount]) { if (![self.quorumRotationService retrievalQueueCount]) [self removeOutdatedMasternodeListsBeforeBlockHash:self.store.lastQueriedBlockHash]; diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m index d4cc93789..8bb336468 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListDiffService.m @@ -30,7 +30,7 @@ - (void)composeMasternodeListRequest:(NSOrderedSet *)list { //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]; + DSMasternodeList *masternodeList = [self.delegate masternodeListSerivceDidRequestFileFromBlockHash:self blockHash:blockHash]; if (masternodeList) { [self removeFromRetrievalQueue:blockHashData]; [self checkWaitingForQuorums]; diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.h b/DashSync/shared/Models/Masternode/DSMasternodeListService.h index bb7c4de38..ae1c225f8 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.h +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.h @@ -39,9 +39,9 @@ typedef NS_ENUM(NSUInteger, DSMasternodeListRequestMode) { @protocol DSMasternodeListServiceDelegate -- (DSMasternodeList *__nullable)masternodeListServiceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; -- (void)masternodeListServiceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; -- (void)masternodeListServiceEmptiedRetrievalQueue:(DSMasternodeListService *)service; +- (DSMasternodeList *__nullable)masternodeListSerivceDidRequestFileFromBlockHash:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +- (void)masternodeListSerivceExceededMaxFailuresForMasternodeList:(DSMasternodeListService *)service blockHash:(UInt256)blockHash; +- (void)masternodeListSerivceEmptiedRetrievalQueue:(DSMasternodeListService *)service; @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.m b/DashSync/shared/Models/Masternode/DSMasternodeListService.m index 5dcc72d68..17c7dd746 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.m @@ -312,7 +312,7 @@ - (void)updateMasternodeRetrievalQueue { - (void)fetchMasternodeListsToRetrieve:(void (^)(NSOrderedSet *listsToRetrieve))completion { if (![self retrievalQueueCount]) { DSLog(@"[%@] No masternode lists in retrieval: %@", self.chain.name, self); - [self.delegate masternodeListServiceEmptiedRetrievalQueue:self]; + [self.delegate masternodeListSerivceEmptiedRetrievalQueue:self]; return; } if ([self.requestsInRetrieval count]) { @@ -385,7 +385,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.delegate masternodeListSerivceExceededMaxFailuresForMasternodeList:self blockHash:self.currentMasternodeList.blockHash]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS]; [self getRecentMasternodeList]; } else { diff --git a/DashSync/shared/dpns-contract.json b/DashSync/shared/dpns-contract.json index b620bb79c..81692dd88 100644 --- a/DashSync/shared/dpns-contract.json +++ b/DashSync/shared/dpns-contract.json @@ -1,14 +1,8 @@ { "documents": { "domain": { - "documentsMutable": false, - "canBeDeleted": true, - "transferable": 1, - "tradeMode": 1, - "type": "object", "indices": [ { - "name": "parentNameAndLabel", "properties": [ { "normalizedParentDomainName": "asc" @@ -17,24 +11,20 @@ "normalizedLabel": "asc" } ], - "unique": true, - "contested": { - "fieldMatches": [ - { - "field": "normalizedLabel", - "regexPattern": "^[a-zA-Z01-]{3,19}$" - } - ], - "resolution": 0, - "description": "If the normalized label part of this index is less than 20 characters (all alphabet a-z, A-Z, 0, 1, and -) then a masternode vote contest takes place to give out the name" - } + "unique": true }, { - "name": "identityId", - "nullSearchable": false, "properties": [ { - "records.identity": "asc" + "records.dashUniqueIdentityId": "asc" + } + ], + "unique": true + }, + { + "properties": [ + { + "records.dashAliasIdentityId": "asc" } ] } @@ -42,60 +32,58 @@ "properties": { "label": { "type": "string", - "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$", + "pattern": "^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9])$", "minLength": 3, "maxLength": 63, - "position": 0, "description": "Domain label. e.g. 'Bob'." }, "normalizedLabel": { "type": "string", - "pattern": "^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-]{0,61}[a-hj-km-np-z0-9]$", + "pattern": "^((?!-)[a-z0-9-]{0,62}[a-z0-9])$", "maxLength": 63, - "position": 1, - "description": "Domain label converted to lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'b0b'", - "$comment": "Must be equal to the label in lowercase. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\"." - }, - "parentDomainName": { - "type": "string", - "pattern": "^$|^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$", - "minLength": 0, - "maxLength": 63, - "position": 2, - "description": "A full parent domain name. e.g. 'dash'." + "description": "Domain label in lowercase for case-insensitive uniqueness validation. e.g. 'bob'", + "$comment": "Must be equal to the label in lowercase. This property will be deprecated due to case insensitive indices" }, "normalizedParentDomainName": { "type": "string", - "pattern": "^$|^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-\\.]{0,61}[a-hj-km-np-z0-9]$", + "pattern": "^$|^((?!-)[a-z0-9-\\.]{0,189}[a-z0-9])$", "minLength": 0, - "maxLength": 63, - "position": 3, - "description": "A parent domain name in lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'dash'", - "$comment": "Must either be equal to an existing domain or empty to create a top level domain. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\". Only the data contract owner can create top level domains." + "maxLength": 190, + "description": "A full parent domain name in lowercase for case-insensitive uniqueness validation. e.g. 'dash'", + "$comment": "Must either be equal to an existing domain or empty to create a top level domain. Only the data contract owner can create top level domains." }, "preorderSalt": { "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32, - "position": 4, "description": "Salt used in the preorder document" }, "records": { "type": "object", "properties": { - "identity": { + "dashUniqueIdentityId": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 32, + "contentMediaType": "application/x.dash.dpp.identifier", + "description": "Identity ID to be used to create the primary name the Identity", + "$comment": "Must be equal to the document owner" + }, + "dashAliasIdentityId": { "type": "array", "byteArray": true, "minItems": 32, "maxItems": 32, - "position": 1, "contentMediaType": "application/x.dash.dpp.identifier", - "description": "Identifier name record that refers to an Identity" + "description": "Identity ID to be used to create alias names for the Identity", + "$comment": "Must be equal to the document owner" } }, + "$comment": "Constraint with max and min properties ensure that only one identity record is used - either a `dashUniqueIdentityId` or a `dashAliasIdentityId`", "minProperties": 1, - "position": 5, + "maxProperties": 1, "additionalProperties": false }, "subdomainRules": { @@ -104,22 +92,15 @@ "allowSubdomains": { "type": "boolean", "description": "This option defines who can create subdomains: true - anyone; false - only the domain owner", - "$comment": "Only the domain owner is allowed to create subdomains for non top-level domains", - "position": 0 + "$comment": "Only the domain owner is allowed to create subdomains for non top-level domains" } }, - "position": 6, "description": "Subdomain rules allow domain owners to define rules for subdomains", "additionalProperties": false, - "required": [ - "allowSubdomains" - ] + "required": ["allowSubdomains"] } }, "required": [ - "$createdAt", - "$updatedAt", - "$transferredAt", "label", "normalizedLabel", "normalizedParentDomainName", @@ -127,19 +108,12 @@ "records", "subdomainRules" ], - "transient": [ - "preorderSalt" - ], "additionalProperties": false, "$comment": "In order to register a domain you need to create a preorder. The preorder step is needed to prevent man-in-the-middle attacks. normalizedLabel + '.' + normalizedParentDomain must not be longer than 253 chars length as defined by RFC 1035. Domain documents are immutable: modification and deletion are restricted" }, "preorder": { - "documentsMutable": false, - "canBeDeleted": true, - "type": "object", "indices": [ { - "name": "saltedHash", "properties": [ { "saltedDomainHash": "asc" @@ -154,7 +128,6 @@ "byteArray": true, "minItems": 32, "maxItems": 32, - "position": 0, "description": "Double sha-256 of the concatenation of a 32 byte random salt and a normalized domain name" } }, @@ -165,4 +138,4 @@ "$comment": "Preorder documents are immutable: modification and deletion are restricted" } } -} \ No newline at end of file +} diff --git a/Example/DashSync.xcodeproj/project.pbxproj b/Example/DashSync.xcodeproj/project.pbxproj index dc8216820..53cc4cd0a 100644 --- a/Example/DashSync.xcodeproj/project.pbxproj +++ b/Example/DashSync.xcodeproj/project.pbxproj @@ -2773,10 +2773,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 +2817,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; @@ -3242,6 +3250,7 @@ "-l\"BoringSSL-GRPC-iOS\"", "-l\"CocoaImageHashing-iOS\"", "-l\"CocoaLumberjack-iOS\"", + "-l\"DAPI-GRPC-iOS\"", "-l\"DSDynamicOptions-iOS\"", "-l\"DWAlertController\"", "-l\"DashSync-iOS\"", @@ -3303,6 +3312,7 @@ "-l\"BoringSSL-GRPC-iOS\"", "-l\"CocoaImageHashing-iOS\"", "-l\"CocoaLumberjack-iOS\"", + "-l\"DAPI-GRPC-iOS\"", "-l\"DSDynamicOptions-iOS\"", "-l\"DWAlertController\"", "-l\"DashSync-iOS\"", diff --git a/Example/Podfile.lock b/Example/Podfile.lock index d9b4e0e7d..c12111213 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,18 +1,663 @@ PODS: + - "!ProtoCompiler (3.21.5)": + - Protobuf (~> 3.0) + - "!ProtoCompiler-gRPCPlugin (1.49.0)": + - "!ProtoCompiler (= 3.21.5)" + - gRPC-ProtoRPC (= 1.49.0) + - abseil/algorithm/algorithm (1.20220623.0): + - abseil/base/config + - abseil/algorithm/container (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/base/atomic_hook (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/base (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/base/base_internal (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/base/config (1.20220623.0) + - abseil/base/core_headers (1.20220623.0): + - abseil/base/config + - abseil/base/dynamic_annotations (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver (1.20220623.0): + - abseil/base/config + - abseil/base/fast_type_id (1.20220623.0): + - abseil/base/config + - abseil/base/log_severity (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal (1.20220623.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/base/prefetch (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/log_severity + - abseil/base/spinlock_wait (1.20220623.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/strerror (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/throw_delegate (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/container/common (1.20220623.0): + - abseil/meta/type_traits + - abseil/types/optional + - abseil/container/compressed_tuple (1.20220623.0): + - abseil/utility/utility + - abseil/container/container_memory (1.20220623.0): + - abseil/base/config + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/container/fixed_array (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/container/flat_hash_map (1.20220623.0): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_map + - abseil/memory/memory + - abseil/container/flat_hash_set (1.20220623.0): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_function_defaults + - abseil/container/raw_hash_set + - abseil/memory/memory + - abseil/container/hash_function_defaults (1.20220623.0): + - abseil/base/config + - abseil/hash/hash + - abseil/strings/cord + - abseil/strings/strings + - abseil/container/hash_policy_traits (1.20220623.0): + - abseil/meta/type_traits + - abseil/container/hashtable_debug_hooks (1.20220623.0): + - abseil/base/config + - abseil/container/hashtablez_sampler (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/stacktrace + - abseil/memory/memory + - abseil/profiling/exponential_biased + - abseil/profiling/sample_recorder + - abseil/synchronization/synchronization + - abseil/utility/utility + - abseil/container/inlined_vector (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/container/inlined_vector_internal (1.20220623.0): + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/container/layout (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/utility/utility + - abseil/container/raw_hash_map (1.20220623.0): + - abseil/base/throw_delegate + - abseil/container/container_memory + - abseil/container/raw_hash_set + - abseil/container/raw_hash_set (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/prefetch + - abseil/container/common + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/hash_policy_traits + - abseil/container/hashtable_debug_hooks + - abseil/container/hashtablez_sampler + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/utility/utility + - abseil/debugging/debugging_internal (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/errno_saver + - abseil/base/raw_logging_internal + - abseil/debugging/demangle_internal (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/stacktrace (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/debugging_internal + - abseil/debugging/symbolize (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/debugging/demangle_internal + - abseil/strings/strings + - abseil/functional/any_invocable (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/functional/bind_front (1.20220623.0): + - abseil/base/base_internal + - abseil/container/compressed_tuple + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/functional/function_ref (1.20220623.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/hash/city (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/hash/hash (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/container/fixed_array + - abseil/functional/function_ref + - abseil/hash/city + - abseil/hash/low_level_hash + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/optional + - abseil/types/variant + - abseil/utility/utility + - abseil/hash/low_level_hash (1.20220623.0): + - abseil/base/config + - abseil/base/endian + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/memory/memory (1.20220623.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/meta/type_traits (1.20220623.0): + - abseil/base/config + - abseil/numeric/bits (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/int128 (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/bits + - abseil/numeric/representation (1.20220623.0): + - abseil/base/config + - abseil/profiling/exponential_biased (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/profiling/sample_recorder (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/random/distributions (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/distribution_caller + - abseil/random/internal/fast_uniform_bits + - abseil/random/internal/fastmath + - abseil/random/internal/generate_real + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/traits + - abseil/random/internal/uniform_helper + - abseil/random/internal/wide_multiply + - abseil/strings/strings + - abseil/random/internal/distribution_caller (1.20220623.0): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/utility/utility + - abseil/random/internal/fast_uniform_bits (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/random/internal/traits + - abseil/random/internal/fastmath (1.20220623.0): + - abseil/numeric/bits + - abseil/random/internal/generate_real (1.20220623.0): + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/fastmath + - abseil/random/internal/traits + - abseil/random/internal/iostream_state_saver (1.20220623.0): + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/random/internal/nonsecure_base (1.20220623.0): + - abseil/base/core_headers + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/types/span + - abseil/random/internal/pcg_engine (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/fastmath + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/platform (1.20220623.0): + - abseil/base/config + - abseil/random/internal/pool_urbg (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/random/internal/randen + - abseil/random/internal/seed_material + - abseil/random/internal/traits + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/random/internal/randen (1.20220623.0): + - abseil/base/raw_logging_internal + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes + - abseil/random/internal/randen_slow + - abseil/random/internal/randen_engine (1.20220623.0): + - abseil/base/endian + - abseil/meta/type_traits + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/randen + - abseil/random/internal/randen_hwaes (1.20220623.0): + - abseil/base/config + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes_impl + - abseil/random/internal/randen_hwaes_impl (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/random/internal/randen_slow (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/random/internal/salted_seed_seq (1.20220623.0): + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/seed_material + - abseil/types/optional + - abseil/types/span + - abseil/random/internal/seed_material (1.20220623.0): + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/random/internal/fast_uniform_bits + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/random/internal/traits (1.20220623.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/uniform_helper (1.20220623.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/random/internal/wide_multiply (1.20220623.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/random/random (1.20220623.0): + - abseil/random/distributions + - abseil/random/internal/nonsecure_base + - abseil/random/internal/pcg_engine + - abseil/random/internal/pool_urbg + - abseil/random/internal/randen_engine + - abseil/random/seed_sequences + - abseil/random/seed_gen_exception (1.20220623.0): + - abseil/base/config + - abseil/random/seed_sequences (1.20220623.0): + - abseil/base/config + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/status/status (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/base/strerror + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/functional/function_ref + - abseil/strings/cord + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/status/statusor (1.20220623.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/status/status + - abseil/strings/strings + - abseil/types/variant + - abseil/utility/utility + - abseil/strings/cord (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/container/fixed_array + - abseil/container/inlined_vector + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_info + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_scope + - abseil/strings/cordz_update_tracker + - abseil/strings/internal + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/strings/cord_internal (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/container/inlined_vector + - abseil/container/layout + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/strings/cordz_functions (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/profiling/exponential_biased + - abseil/strings/cordz_handle (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/synchronization/synchronization + - abseil/strings/cordz_info (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_handle + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_tracker + - abseil/synchronization/synchronization + - abseil/types/span + - abseil/strings/cordz_statistics (1.20220623.0): + - abseil/base/config + - abseil/strings/cordz_update_tracker + - abseil/strings/cordz_update_scope (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/cord_internal + - abseil/strings/cordz_info + - abseil/strings/cordz_update_tracker + - abseil/strings/cordz_update_tracker (1.20220623.0): + - abseil/base/config + - abseil/strings/internal (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/strings/str_format (1.20220623.0): + - abseil/strings/str_format_internal + - abseil/strings/str_format_internal (1.20220623.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/numeric/representation + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/utility/utility + - abseil/strings/strings (1.20220623.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/strings/internal + - abseil/synchronization/graphcycles_internal (1.20220623.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/synchronization/kernel_timeout_internal (1.20220623.0): + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/time/time + - abseil/synchronization/synchronization (1.20220623.0): + - abseil/base/atomic_hook + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/synchronization/graphcycles_internal + - abseil/synchronization/kernel_timeout_internal + - abseil/time/time + - abseil/time/internal/cctz/civil_time (1.20220623.0): + - abseil/base/config + - abseil/time/internal/cctz/time_zone (1.20220623.0): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/time/time (1.20220623.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types/bad_optional_access (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_variant_access (1.20220623.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/optional (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/types/span (1.20220623.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/types/variant (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/utility/utility (1.20220623.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - BoringSSL-GRPC (0.0.24): + - BoringSSL-GRPC/Implementation (= 0.0.24) + - BoringSSL-GRPC/Interface (= 0.0.24) + - BoringSSL-GRPC/Implementation (0.0.24): + - BoringSSL-GRPC/Interface (= 0.0.24) + - BoringSSL-GRPC/Interface (0.0.24) - CocoaImageHashing (1.9.0) - CocoaLumberjack (3.7.2): - CocoaLumberjack/Core (= 3.7.2) - CocoaLumberjack/Core (3.7.2) - - DashSharedCore (0.4.16) + - DAPI-GRPC (0.22.0-dev.8): + - "!ProtoCompiler-gRPCPlugin (~> 1.0)" + - DAPI-GRPC/Messages (= 0.22.0-dev.8) + - DAPI-GRPC/Services (= 0.22.0-dev.8) + - DAPI-GRPC/Messages (0.22.0-dev.8): + - "!ProtoCompiler-gRPCPlugin (~> 1.0)" + - Protobuf + - DAPI-GRPC/Services (0.22.0-dev.8): + - "!ProtoCompiler-gRPCPlugin (~> 1.0)" + - DAPI-GRPC/Messages + - gRPC-ProtoRPC + - DashSharedCore (0.4.19) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - - DashSharedCore (= 0.4.16) + - DAPI-GRPC (= 0.22.0-dev.8) + - DashSharedCore (= 0.4.19) - DSDynamicOptions (= 0.1.2) - DWAlertController (= 0.2.1) - TinyCborObjc (= 0.4.6) - DSDynamicOptions (0.1.2) - DWAlertController (0.2.1) + - gRPC-Core (1.49.0): + - gRPC-Core/Implementation (= 1.49.0) + - gRPC-Core/Interface (= 1.49.0) + - gRPC-Core/Implementation (1.49.0): + - abseil/base/base (= 1.20220623.0) + - abseil/base/core_headers (= 1.20220623.0) + - abseil/container/flat_hash_map (= 1.20220623.0) + - abseil/container/flat_hash_set (= 1.20220623.0) + - abseil/container/inlined_vector (= 1.20220623.0) + - abseil/functional/any_invocable (= 1.20220623.0) + - abseil/functional/bind_front (= 1.20220623.0) + - abseil/functional/function_ref (= 1.20220623.0) + - abseil/hash/hash (= 1.20220623.0) + - abseil/memory/memory (= 1.20220623.0) + - abseil/meta/type_traits (= 1.20220623.0) + - abseil/random/random (= 1.20220623.0) + - abseil/status/status (= 1.20220623.0) + - abseil/status/statusor (= 1.20220623.0) + - abseil/strings/cord (= 1.20220623.0) + - abseil/strings/str_format (= 1.20220623.0) + - abseil/strings/strings (= 1.20220623.0) + - abseil/synchronization/synchronization (= 1.20220623.0) + - abseil/time/time (= 1.20220623.0) + - abseil/types/optional (= 1.20220623.0) + - abseil/types/span (= 1.20220623.0) + - abseil/types/variant (= 1.20220623.0) + - abseil/utility/utility (= 1.20220623.0) + - BoringSSL-GRPC (= 0.0.24) + - gRPC-Core/Interface (= 1.49.0) + - gRPC-Core/Interface (1.49.0) + - gRPC-ProtoRPC (1.49.0): + - gRPC-ProtoRPC/Legacy (= 1.49.0) + - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) + - gRPC-ProtoRPC/Main (= 1.49.0) + - gRPC-ProtoRPC/Legacy (1.49.0): + - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) + - gRPC-ProtoRPC/Main (= 1.49.0) + - gRPC-RxLibrary (= 1.49.0) + - gRPC/GRPCCore (= 1.49.0) + - Protobuf (~> 3.0) + - gRPC-ProtoRPC/Legacy-Header (1.49.0) + - gRPC-ProtoRPC/Main (1.49.0): + - gRPC-ProtoRPC/Legacy-Header (= 1.49.0) + - gRPC/Interface (= 1.49.0) + - Protobuf (~> 3.0) + - gRPC-RxLibrary (1.49.0): + - gRPC-RxLibrary/Implementation (= 1.49.0) + - gRPC-RxLibrary/Interface (= 1.49.0) + - gRPC-RxLibrary/Implementation (1.49.0): + - gRPC-RxLibrary/Interface + - gRPC-RxLibrary/Interface (1.49.0) + - gRPC/GRPCCore (1.49.0): + - gRPC-Core (= 1.49.0) + - gRPC-RxLibrary (= 1.49.0) + - gRPC/Interface (= 1.49.0) + - gRPC/Interface-Legacy (= 1.49.0) + - gRPC/Interface (1.49.0): + - gRPC/Interface-Legacy (= 1.49.0) + - gRPC/Interface-Legacy (1.49.0): + - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) + - Protobuf (3.28.3) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -28,11 +673,21 @@ DEPENDENCIES: SPEC REPOS: trunk: + - "!ProtoCompiler" + - "!ProtoCompiler-gRPCPlugin" + - abseil + - BoringSSL-GRPC - CocoaLumberjack + - DAPI-GRPC - DashSharedCore - DSDynamicOptions - DWAlertController + - gRPC + - gRPC-Core + - gRPC-ProtoRPC + - gRPC-RxLibrary - KVO-MVVM + - Protobuf - SDWebImage - tinycbor - TinyCborObjc @@ -50,13 +705,23 @@ CHECKOUT OPTIONS: :git: https://github.com/ameingast/cocoaimagehashing.git SPEC CHECKSUMS: + "!ProtoCompiler": e9c09244955a8565817aa59a4787b6bb849a63c6 + "!ProtoCompiler-gRPCPlugin": 755f0ee414a0d5f0028e0dcfe98c23bdbc3e6fa3 + abseil: 926fb7a82dc6d2b8e1f2ed7f3a718bce691d1e46 + BoringSSL-GRPC: 3175b25143e648463a56daeaaa499c6cb86dad33 CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da - DashSharedCore: 81d3327cbea4103114b768eed4d36e742417b63b - DashSync: dd9d652b73938fbef99137b3d60e12962ca915ea + DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f + DashSharedCore: 009f29640756017406ee2b0ab5f1073f1856f85e + DashSync: f0ee76fe1409c9071bcee21202cc8002944c801d DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b + gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f + gRPC-Core: 3a9fdb5967d42211e875826f3f6fc163ea02c2a1 + gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d + gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 + Protobuf: 5a8a7781d8e1004302f108977ac2d5b99323146f SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 diff --git a/Gemfile b/Gemfile index 2af168f6e..a289656a4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gem 'rexml' -gem 'cocoapods', '>=1.15.2' +gem 'cocoapods', '>=1.11.3' gem 'xcpretty' # ethon is manually updated to avoid segmentation fault crashes on the M1. See https://github.com/CocoaPods/CocoaPods/issues/10446 gem 'ethon', '>=0.13.0' From 68ccdd786d7d428b861e8395694228b4bb021336 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sat, 23 Nov 2024 21:16:45 +0700 Subject: [PATCH 072/133] fix: adjust coinjoin gap limit when forming the bloom filter --- DashSync/shared/Models/Chain/DSChain.m | 4 ++-- DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m | 2 +- DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h | 2 +- DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m | 6 +++--- .../Managers/Chain Managers/DSTransactionManager.m | 4 ++-- DashSync/shared/Models/Wallet/DSAccount.h | 2 +- DashSync/shared/Models/Wallet/DSAccount.m | 13 +++++++++++-- DashSync/shared/Models/Wallet/DSWallet.h | 2 +- DashSync/shared/Models/Wallet/DSWallet.m | 4 ++-- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/DashSync/shared/Models/Chain/DSChain.m b/DashSync/shared/Models/Chain/DSChain.m index 497f9e1a5..cefb7a469 100644 --- a/DashSync/shared/Models/Chain/DSChain.m +++ b/DashSync/shared/Models/Chain/DSChain.m @@ -1230,8 +1230,8 @@ - (DSBloomFilter *)bloomFilterWithFalsePositiveRate:(double)falsePositiveRate wi // every time a new wallet address is added, the bloom filter has to be rebuilt, and each address is only used for // one transaction, so here we generate some spare addresses to avoid rebuilding the filter each time a wallet // transaction is encountered during the blockchain download - [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_INITIAL unusedAccountGapLimit:SEQUENCE_UNUSED_GAP_LIMIT_INITIAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL internal:NO error:nil]; - [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_INITIAL unusedAccountGapLimit:SEQUENCE_UNUSED_GAP_LIMIT_INITIAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL internal:YES error:nil]; + [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_INITIAL unusedAccountGapLimit:SEQUENCE_UNUSED_GAP_LIMIT_INITIAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL coinJoinGapLimit:SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN internal:NO error:nil]; + [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_INITIAL unusedAccountGapLimit:SEQUENCE_UNUSED_GAP_LIMIT_INITIAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INITIAL coinJoinGapLimit:SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN internal:YES error:nil]; NSSet *addresses = [wallet.allReceiveAddresses setByAddingObjectsFromSet:wallet.allChangeAddresses]; [allAddresses addObjectsFromArray:[addresses allObjects]]; [allUTXOs addObjectsFromArray:wallet.unspentOutputs]; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index 937bbdf55..a093c1e89 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -895,7 +895,7 @@ - (BOOL)isWaitingForNewBlock { } - (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { - return [self.wrapper coinJoinTxTypeForTransaction:transaction]; + return [DSCoinJoinWrapper coinJoinTxTypeForTransaction:transaction]; } - (void)calculateAnonymizableBalanceWithSkipDenominated:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed completion:(void (^)(uint64_t balance))completion { diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 50a1b2324..2acdacb1b 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -44,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)refreshUnusedKeys; - (BOOL)isDenominatedAmount:(uint64_t)amount; - (BOOL)isFullyMixed:(DSUTXO)utxo; -- (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; ++ (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; - (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; - (uint64_t)getSmallestDenomination; - (void)updateOptions:(CoinJoinClientOptions *)options; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 5d137badd..d313bfab1 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -156,11 +156,11 @@ - (BOOL)isMixingFeeTx:(UInt256)txId { } } -- (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:transaction]; ++ (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { + DSAccount *account = [transaction.chain firstAccountThatCanContainTransaction:transaction]; NSArray *amountsSent = [account amountsSentByTransaction:transaction]; - Transaction *tx = [transaction ffi_malloc:self.chain.chainType]; + Transaction *tx = [transaction ffi_malloc:transaction.chain.chainType]; uint64_t *inputValues = malloc(amountsSent.count * sizeof(uint64_t)); for (uintptr_t i = 0; i < amountsSent.count; i++) { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 6d9191ef6..1ca2fadc1 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -1013,8 +1013,8 @@ - (void)updateTransactionsBloomFilter { // every time a new wallet address is added, the bloom filter has to be rebuilt, and each address is only used for // one transaction, so here we generate some spare addresses to avoid rebuilding the filter each time a wallet // transaction is encountered during the blockchain download - [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_EXTERNAL unusedAccountGapLimit:SEQUENCE_UNUSED_GAP_LIMIT_EXTERNAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INCOMING internal:NO error:nil]; - [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_INTERNAL unusedAccountGapLimit:SEQUENCE_GAP_LIMIT_INTERNAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INCOMING internal:YES error:nil]; + [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_EXTERNAL unusedAccountGapLimit:SEQUENCE_UNUSED_GAP_LIMIT_EXTERNAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INCOMING coinJoinGapLimit:SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN internal:NO error:nil]; + [wallet registerAddressesWithGapLimit:SEQUENCE_GAP_LIMIT_INTERNAL unusedAccountGapLimit:SEQUENCE_GAP_LIMIT_INTERNAL dashpayGapLimit:SEQUENCE_DASHPAY_GAP_LIMIT_INCOMING coinJoinGapLimit:SEQUENCE_GAP_LIMIT_INITIAL_COINJOIN internal:YES error:nil]; NSSet *addresses = [wallet.allReceiveAddresses setByAddingObjectsFromSet:wallet.allChangeAddresses]; [allAddressesArray addObjectsFromArray:[addresses allObjects]]; diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index 2a82dfed9..cb261c2f4 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -115,7 +115,7 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // 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; +- (NSArray *_Nullable)registerAddressesWithGapLimit:(NSUInteger)gapLimit unusedAccountGapLimit:(NSUInteger)unusedAccountGapLimit dashpayGapLimit:(NSUInteger)dashpayGapLimit coinJoinGapLimit:(NSUInteger)coinJoinGapLimit internal:(BOOL)internal error:(NSError **)error; + (DSAccount *)accountWithAccountNumber:(uint32_t)accountNumber withDerivationPaths:(NSArray *)derivationPaths inContext:(NSManagedObjectContext *_Nullable)context; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 73fbfc85b..5e4835587 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -481,12 +481,21 @@ - (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 coinJoinGapLimit:(NSUInteger)coinJoinGapLimit internal:(BOOL)internal error:(NSError **)error { NSMutableArray *mArray = [NSMutableArray array]; for (DSDerivationPath *derivationPath in self.fundDerivationPaths) { if ([derivationPath isKindOfClass:[DSFundsDerivationPath class]]) { DSFundsDerivationPath *fundsDerivationPath = (DSFundsDerivationPath *)derivationPath; - NSUInteger registerGapLimit = [fundsDerivationPath shouldUseReducedGapLimit] ? unusedAccountGapLimit : gapLimit; + NSUInteger registerGapLimit = 0; + + if ([fundsDerivationPath shouldUseReducedGapLimit]) { + registerGapLimit = unusedAccountGapLimit; + } else if (fundsDerivationPath.type == DSDerivationPathType_AnonymousFunds) { + registerGapLimit = coinJoinGapLimit; + } else { + registerGapLimit = gapLimit; + } + [mArray addObjectsFromArray:[fundsDerivationPath registerAddressesWithGapLimit:registerGapLimit internal:internal error:error]]; } else if (!internal && [derivationPath isKindOfClass:[DSIncomingFundsDerivationPath class]]) { [mArray addObjectsFromArray:[(DSIncomingFundsDerivationPath *)derivationPath registerAddressesWithGapLimit:dashpayGapLimit error:error]]; diff --git a/DashSync/shared/Models/Wallet/DSWallet.h b/DashSync/shared/Models/Wallet/DSWallet.h index 229350574..2416a1154 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.h +++ b/DashSync/shared/Models/Wallet/DSWallet.h @@ -175,7 +175,7 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSWalletBalanceDidChangeNotification; // 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 coinJoinGapLimit:(NSUInteger)coinJoinGapLimit 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; diff --git a/DashSync/shared/Models/Wallet/DSWallet.m b/DashSync/shared/Models/Wallet/DSWallet.m index 25b213fc3..4a619cc4c 100644 --- a/DashSync/shared/Models/Wallet/DSWallet.m +++ b/DashSync/shared/Models/Wallet/DSWallet.m @@ -855,10 +855,10 @@ - (uint64_t)balance { return rBalance; } -- (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 coinJoinGapLimit:(NSUInteger)coinJoinGapLimit internal:(BOOL)internal error:(NSError **)error { NSMutableArray *mArray = [NSMutableArray array]; for (DSAccount *account in self.accounts) { - [mArray addObjectsFromArray:[account registerAddressesWithGapLimit:gapLimit unusedAccountGapLimit:unusedAccountGapLimit dashpayGapLimit:dashpayGapLimit internal:internal error:error]]; + [mArray addObjectsFromArray:[account registerAddressesWithGapLimit:gapLimit unusedAccountGapLimit:unusedAccountGapLimit dashpayGapLimit:dashpayGapLimit coinJoinGapLimit:coinJoinGapLimit internal:internal error:error]]; } return [mArray copy]; } From 0edaa9600b8f20696ba1fa4c51dc115652ef9913 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 4 Dec 2024 14:39:29 +0700 Subject: [PATCH 073/133] chore: add session status to logs --- DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index a093c1e89..fe2d60dfa 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -962,7 +962,7 @@ - (void)onSessionStarted:(int32_t)baseId clientSessionId:(UInt256)clientId denom } - (void)onSessionComplete:(int32_t)baseId clientSessionId:(UInt256)clientId denomination:(uint32_t)denom poolState:(PoolState)state poolMessage:(PoolMessage)message poolStatus:(PoolStatus)status ipAddress:(UInt128)address isJoined:(BOOL)joined { - DSLog(@"[%@] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); + DSLog(@"[%@] CoinJoin: onSessionComplete: baseId: %d, clientId: %@, denom: %d, state: %d, status: %d, message: %d, address: %@, isJoined: %s", self.chain.name, baseId, [uint256_hex(clientId) substringToIndex:7], denom, state, status, message, [self.masternodeGroup hostFor:address], joined ? "yes" : "no"); [self.managerDelegate sessionCompleteWithId:baseId clientSessionId:clientId denomination:denom poolState:state poolMessage:message poolStatus:status ipAddress:address isJoined:joined]; } From e37280a9b369c25dc14d2001260206c8c21ae271 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 4 Dec 2024 17:21:42 +0700 Subject: [PATCH 074/133] chore: cleanup --- DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m index 76aa55d91..27a61cc02 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransactionOutput+CoinJoin.m @@ -53,7 +53,7 @@ + (void)ffi_free:(TransactionOutput *)output { } if (output->address) { -// processor_destroy_string(output->address); TODO + processor_destroy_string((char *)output->address); } free(output); From 54e7567d98b7fb59dd18edaa357321306437eb7e Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 13 Dec 2024 15:02:35 +0700 Subject: [PATCH 075/133] feat: compress logs --- DashSync/shared/Libraries/DSLogger.m | 41 +- .../Logs/CompressingLogFileManager.h | 20 + .../Logs/CompressingLogFileManager.m | 563 ++++++++++++++++++ DashSync/shared/Models/Network/DSPeer.m | 2 +- 4 files changed, 607 insertions(+), 19 deletions(-) create mode 100644 DashSync/shared/Libraries/Logs/CompressingLogFileManager.h create mode 100644 DashSync/shared/Libraries/Logs/CompressingLogFileManager.m diff --git a/DashSync/shared/Libraries/DSLogger.m b/DashSync/shared/Libraries/DSLogger.m index 5f59dc9c7..2d68e4ee0 100644 --- a/DashSync/shared/Libraries/DSLogger.m +++ b/DashSync/shared/Libraries/DSLogger.m @@ -16,6 +16,7 @@ // #import "DSLogger.h" +#import "CompressingLogFileManager.h" NS_ASSUME_NONNULL_BEGIN @@ -49,10 +50,13 @@ - (instancetype)init { if (self) { [DDLog addLogger:[DDOSLogger sharedInstance]]; // os_log - DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; - fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling - fileLogger.logFileManager.maximumNumberOfLogFiles = 3; // keep a 3 days worth of log files - //[fileLogger setLogFormatter:[[NoTimestampLogFormatter alloc] init]]; // Use the custom formatter + unsigned long long maxFileSize = 1024 * 1024 * 5; // 5 MB max. Then log files are ziped + CompressingLogFileManager *logFileManager = [[CompressingLogFileManager alloc] initWithFileSize:maxFileSize]; + DDFileLogger *fileLogger = [[DDFileLogger alloc] initWithLogFileManager:logFileManager]; + fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling + fileLogger.maximumFileSize = maxFileSize; + fileLogger.logFileManager.maximumNumberOfLogFiles = 10; + [DDLog addLogger:fileLogger]; _fileLogger = fileLogger; } @@ -60,22 +64,23 @@ - (instancetype)init { } - (NSArray *)logFiles { - NSArray *logFileInfos = [self.fileLogger.logFileManager unsortedLogFileInfos]; - NSMutableArray *logFiles = [NSMutableArray array]; - for (DDLogFileInfo *fileInfo in logFileInfos) { - NSURL *fileURL = [NSURL fileURLWithPath:fileInfo.filePath]; - if (fileURL) { - [logFiles addObject:fileURL]; - } - } - // add rust log file located at $CACHE/Logs/processor.log - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - NSString *cacheDirectory = [paths objectAtIndex:0]; - NSString *rustLogPath = [cacheDirectory stringByAppendingPathComponent:@"Logs/processor.log"]; + NSString *logsDirectory = [self.fileLogger.logFileManager logsDirectory]; + NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logsDirectory error:nil]; + NSMutableArray *logFiles = [NSMutableArray arrayWithCapacity:[fileNames count]]; - if ([[NSFileManager defaultManager] fileExistsAtPath:rustLogPath]) { - [logFiles addObject:[NSURL fileURLWithPath:rustLogPath]]; + for (NSString *fileName in fileNames) { + BOOL hasProperSuffix = [fileName hasSuffix:@".log"] || [fileName hasSuffix:@".gz"]; + + if (hasProperSuffix) { + NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName]; + NSURL *fileURL = [NSURL fileURLWithPath:filePath]; + + if (fileURL) { + [logFiles addObject:fileURL]; + } + } } + return [logFiles copy]; } diff --git a/DashSync/shared/Libraries/Logs/CompressingLogFileManager.h b/DashSync/shared/Libraries/Logs/CompressingLogFileManager.h new file mode 100644 index 000000000..8a34b52e2 --- /dev/null +++ b/DashSync/shared/Libraries/Logs/CompressingLogFileManager.h @@ -0,0 +1,20 @@ +// +// CompressingLogFileManager.h +// LogFileCompressor +// +// CocoaLumberjack Demos +// + +#import +#import + +@interface CompressingLogFileManager : DDLogFileManagerDefault +{ + BOOL upToDate; + BOOL isCompressing; +} + +@property (nonatomic, assign) unsigned long long maxFileSize; +- (instancetype)initWithFileSize:(unsigned long long)maxFileSize; + +@end diff --git a/DashSync/shared/Libraries/Logs/CompressingLogFileManager.m b/DashSync/shared/Libraries/Logs/CompressingLogFileManager.m new file mode 100644 index 000000000..e1aaa013c --- /dev/null +++ b/DashSync/shared/Libraries/Logs/CompressingLogFileManager.m @@ -0,0 +1,563 @@ +// +// CompressingLogFileManager.m +// LogFileCompressor +// +// CocoaLumberjack Demos +// + +#import "CompressingLogFileManager.h" +#import "dash_shared_core.h" +#import + +// We probably shouldn't be using DDLog() statements within the DDLog implementation. +// But we still want to leave our log statements for any future debugging, +// and to allow other developers to trace the implementation (which is a great learning tool). +// +// So we use primitive logging macros around NSLog. +// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog. + +#define LOG_LEVEL 4 + +#define NSLogError(frmt, ...) do{ if(LOG_LEVEL >= 1) NSLog(frmt, ##__VA_ARGS__); } while(0) +#define NSLogWarn(frmt, ...) do{ if(LOG_LEVEL >= 2) NSLog(frmt, ##__VA_ARGS__); } while(0) +#define NSLogInfo(frmt, ...) do{ if(LOG_LEVEL >= 3) NSLog(frmt, ##__VA_ARGS__); } while(0) +#define NSLogVerbose(frmt, ...) do{ if(LOG_LEVEL >= 4) NSLog(frmt, ##__VA_ARGS__); } while(0) + +@interface CompressingLogFileManager (/* Must be nameless for properties */) + +@property (readwrite) BOOL isCompressing; + +@end + +@interface DDLogFileInfo (Compressor) + +@property (nonatomic, readonly) BOOL isCompressed; + +- (NSString *)tempFilePathByAppendingPathExtension:(NSString *)newExt; +- (NSString *)fileNameByAppendingPathExtension:(NSString *)newExt; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation CompressingLogFileManager + +@synthesize isCompressing; + +- (instancetype)initWithFileSize:(unsigned long long)maxFileSize { + self = [self initWithLogsDirectory:nil]; + if (self) { + _maxFileSize = maxFileSize; + } + return self; +} + +- (id)init +{ + return [self initWithLogsDirectory:nil]; +} + +- (id)initWithLogsDirectory:(NSString *)aLogsDirectory +{ + if ((self = [super initWithLogsDirectory:aLogsDirectory])) + { + upToDate = NO; + + // Check for any files that need to be compressed. + // But don't start right away. + // Wait for the app startup process to finish. + + [self performSelector:@selector(compressNextLogFile) withObject:nil afterDelay:5.0]; + } + return self; +} + +- (void)dealloc +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(compressNextLogFile) object:nil]; +} + +- (void)compressLogFile:(DDLogFileInfo *)logFile +{ + self.isCompressing = YES; + + CompressingLogFileManager* __weak weakSelf = self; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ + [weakSelf backgroundThread_CompressLogFile:logFile]; + }); +} + +- (void)compressNextLogFile +{ + if (self.isCompressing) + { + // We're already compressing a file. + // Wait until it's done to move onto the next file. + return; + } + + NSLogVerbose(@"CompressingLogFileManager: compressNextLogFile"); + + upToDate = NO; + + NSMutableArray *sortedLogFileInfos = [[self sortedLogFileInfos] mutableCopy]; + [self handleProcessorLogRotationWithSortedLogs:sortedLogFileInfos]; + + NSUInteger count = [sortedLogFileInfos count]; + if (count == 0) + { + // Nothing to compress + upToDate = YES; + return; + } + + NSUInteger i = count; + while (i > 0) + { + DDLogFileInfo *logFileInfo = [sortedLogFileInfos objectAtIndex:(i - 1)]; + + if ((logFileInfo.isArchived || [logFileInfo.fileName hasPrefix:@"processor"]) && !logFileInfo.isCompressed) + { + [self compressLogFile:logFileInfo]; + + break; + } + + i--; + } + + upToDate = YES; +} + +- (void)handleProcessorLogRotationWithSortedLogs:(NSMutableArray *)sortedLogFileInfos { + // Get all processor log files + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSArray *files = [fileManager contentsOfDirectoryAtPath:self.logsDirectory error:nil]; + NSMutableArray *processorLogs = [NSMutableArray array]; + NSMutableArray *compressedLogs = [NSMutableArray array]; + + // Separate processor logs into compressed and uncompressed + for (NSString *file in files) { + if ([file hasPrefix:@"processor."]) { + NSArray *components = [file componentsSeparatedByString:@"."]; + if (components.count >= 3) { + if ([file hasSuffix:@".gz"]) { + [compressedLogs addObject:file]; + } else { + [processorLogs addObject:file]; + } + } + } + } + + // Remove compressed logs older than 5 days + NSDate *cutoffDate = [NSDate dateWithTimeIntervalSinceNow:-5 * 24 * 60 * 60]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setDateFormat:@"yyyy-MM-dd"]; + + for (NSString *file in compressedLogs) { + // Extract date from filename (assuming format processor.[date].log.gz) + NSArray *components = [file componentsSeparatedByString:@"."]; + if (components.count >= 2) { + NSString *dateString = components[1]; + NSDate *fileDate = [dateFormatter dateFromString:dateString]; + + if (fileDate && [fileDate compare:cutoffDate] == NSOrderedAscending) { + NSString *fullPath = [self.logsDirectory stringByAppendingPathComponent:file]; + [fileManager removeItemAtPath:fullPath error:nil]; + } + } + } + + // Handle uncompressed processor logs + if (processorLogs.count > 1) { + // Sort by date (oldest first) + [processorLogs sortUsingComparator:^NSComparisonResult(NSString *file1, NSString *file2) { + return [file1 compare:file2]; + }]; + + // Add older logs to sortedLogFileInfos for compression + for (NSInteger i = 0; i < processorLogs.count - 1; i++) { + NSString *logPath = [self.logsDirectory stringByAppendingPathComponent:processorLogs[i]]; + [sortedLogFileInfos addObject:[DDLogFileInfo logFileWithPath:logPath]]; + } + } +} + +- (void)compressionDidSucceed:(DDLogFileInfo *)logFile +{ + NSLogVerbose(@"CompressingLogFileManager: compressionDidSucceed: %@", logFile.fileName); + + self.isCompressing = NO; + + [self compressNextLogFile]; +} + +- (void)compressionDidFail:(DDLogFileInfo *)logFile +{ + NSLogWarn(@"CompressingLogFileManager: compressionDidFail: %@", logFile.fileName); + + self.isCompressing = NO; + + // We should try the compression again, but after a short delay. + // + // If the compression failed there is probably some filesystem issue, + // so flooding it with compression attempts is only going to make things worse. + + NSTimeInterval delay = (60 * 15); // 15 minutes + + [self performSelector:@selector(compressNextLogFile) withObject:nil afterDelay:delay]; +} + +- (void)didArchiveLogFile:(NSString *)logFilePath wasRolled:(BOOL)wasRolled { + NSLogVerbose(@"CompressingLogFileManager: didArchiveLogFile: %@ wasRolled: %@", + [logFilePath lastPathComponent], (wasRolled ? @"YES" : @"NO")); + + // If all other log files have been compressed, then we can get started right away. + // Otherwise we should just wait for the current compression process to finish. + + if (upToDate) + { + [self compressLogFile:[DDLogFileInfo logFileWithPath:logFilePath]]; + } +} + +- (void)backgroundThread_CompressLogFile:(DDLogFileInfo *)logFile +{ + @autoreleasepool { + + NSLogInfo(@"CompressingLogFileManager: Compressing log file: %@", logFile.fileName); + + // Steps: + // 1. Create a new file with the same fileName, but added "gzip" extension + // 2. Open the new file for writing (output file) + // 3. Open the given file for reading (input file) + // 4. Setup zlib for gzip compression + // 5. Read a chunk of the given file + // 6. Compress the chunk + // 7. Write the compressed chunk to the output file + // 8. Repeat steps 5 - 7 until the input file is exhausted + // 9. Close input and output file + // 10. Teardown zlib + + + // STEP 1 + + NSString *inputFilePath = logFile.filePath; + + NSString *tempOutputFilePath = [logFile tempFilePathByAppendingPathExtension:@"gz"]; + +#if TARGET_OS_IPHONE + // We use the same protection as the original file. This means that it has the same security characteristics. + // Also, if the app can run in the background, this means that it gets + // NSFileProtectionCompleteUntilFirstUserAuthentication so that we can do this compression even with the + // device locked. c.f. DDFileLogger.doesAppRunInBackground. + NSString* protection = logFile.fileAttributes[NSFileProtectionKey]; + NSDictionary* attributes = protection == nil ? nil : @{NSFileProtectionKey: protection}; + [[NSFileManager defaultManager] createFileAtPath:tempOutputFilePath contents:nil attributes:attributes]; +#endif + + // STEP 2 & 3 + + NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:inputFilePath]; + NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:tempOutputFilePath append:NO]; + + [inputStream open]; + [outputStream open]; + + // STEP 4 + + z_stream strm; + + // Zero out the structure before (to be safe) before we start using it + bzero(&strm, sizeof(strm)); + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.total_out = 0; + + // Compresssion Levels: + // Z_NO_COMPRESSION + // Z_BEST_SPEED + // Z_BEST_COMPRESSION + // Z_DEFAULT_COMPRESSION + + deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, (15+16), 9, Z_DEFAULT_STRATEGY); + + // Prepare our variables for steps 5-7 + // + // inputDataLength : Total length of buffer that we will read file data into + // outputDataLength : Total length of buffer that zlib will output compressed bytes into + // + // Note: The output buffer can be smaller than the input buffer because the + // compressed/output data is smaller than the file/input data (obviously). + // + // inputDataSize : The number of bytes in the input buffer that have valid data to be compressed. + // + // Imagine compressing a tiny file that is actually smaller than our inputDataLength. + // In this case only a portion of the input buffer would have valid file data. + // The inputDataSize helps represent the portion of the buffer that is valid. + // + // Imagine compressing a huge file, but consider what happens when we get to the very end of the file. + // The last read will likely only fill a portion of the input buffer. + // The inputDataSize helps represent the portion of the buffer that is valid. + + NSUInteger inputDataLength = (1024 * 64); // 64 KB + NSUInteger outputDataLength = (1024 * 32); // 32 KB + + NSMutableData *inputData = [NSMutableData dataWithLength:inputDataLength]; + NSMutableData *outputData = [NSMutableData dataWithLength:outputDataLength]; + + NSUInteger inputDataSize = 0; + + BOOL done = YES; + NSError* error = nil; + do + { + @autoreleasepool { + + // STEP 5 + // Read data from the input stream into our input buffer. + // + // inputBuffer : pointer to where we want the input stream to copy bytes into + // inputBufferLength : max number of bytes the input stream should read + // + // Recall that inputDataSize is the number of valid bytes that already exist in the + // input buffer that still need to be compressed. + // This value is usually zero, but may be larger if a previous iteration of the loop + // was unable to compress all the bytes in the input buffer. + // + // For example, imagine that we ready 2K worth of data from the file in the last loop iteration, + // but when we asked zlib to compress it all, zlib was only able to compress 1.5K of it. + // We would still have 0.5K leftover that still needs to be compressed. + // We want to make sure not to skip this important data. + // + // The [inputData mutableBytes] gives us a pointer to the beginning of the underlying buffer. + // When we add inputDataSize we get to the proper offset within the buffer + // at which our input stream can start copying bytes into without overwriting anything it shouldn't. + + const void *inputBuffer = [inputData mutableBytes] + inputDataSize; + NSUInteger inputBufferLength = inputDataLength - inputDataSize; + + NSInteger readLength = [inputStream read:(uint8_t *)inputBuffer maxLength:inputBufferLength]; + if (readLength < 0) { + error = [inputStream streamError]; + break; + } + + NSLogVerbose(@"CompressingLogFileManager: Read %li bytes from file", (long)readLength); + + inputDataSize += readLength; + + // STEP 6 + // Ask zlib to compress our input buffer. + // Tell it to put the compressed bytes into our output buffer. + + strm.next_in = (Bytef *)[inputData mutableBytes]; // Read from input buffer + strm.avail_in = (uInt)inputDataSize; // as much as was read from file (plus leftovers). + + strm.next_out = (Bytef *)[outputData mutableBytes]; // Write data to output buffer + strm.avail_out = (uInt)outputDataLength; // as much space as is available in the buffer. + + // When we tell zlib to compress our data, + // it won't directly tell us how much data was processed. + // Instead it keeps a running total of the number of bytes it has processed. + // In other words, every iteration from the loop it increments its total values. + // So to figure out how much data was processed in this iteration, + // we fetch the totals before we ask it to compress data, + // and then afterwards we subtract from the new totals. + + NSUInteger prevTotalIn = strm.total_in; + NSUInteger prevTotalOut = strm.total_out; + + int flush = [inputStream hasBytesAvailable] ? Z_SYNC_FLUSH : Z_FINISH; + deflate(&strm, flush); + + NSUInteger inputProcessed = strm.total_in - prevTotalIn; + NSUInteger outputProcessed = strm.total_out - prevTotalOut; + + NSLogVerbose(@"CompressingLogFileManager: Total bytes uncompressed: %lu", (unsigned long)strm.total_in); + NSLogVerbose(@"CompressingLogFileManager: Total bytes compressed: %lu", (unsigned long)strm.total_out); + NSLogVerbose(@"CompressingLogFileManager: Compression ratio: %.1f%%", + (double)(1.0F - (float)(strm.total_out) / (float)(strm.total_in)) * 100); + + // STEP 7 + // Now write all compressed bytes to our output stream. + // + // It is theoretically possible that the write operation doesn't write everything we ask it to. + // Although this is highly unlikely, we take precautions. + // Also, we watch out for any errors (maybe the disk is full). + + NSUInteger totalWriteLength = 0; + NSInteger writeLength = 0; + + do + { + const void *outputBuffer = [outputData mutableBytes] + totalWriteLength; + NSUInteger outputBufferLength = outputProcessed - totalWriteLength; + + writeLength = [outputStream write:(const uint8_t *)outputBuffer maxLength:outputBufferLength]; + + if (writeLength < 0) + { + error = [outputStream streamError]; + } + else + { + totalWriteLength += writeLength; + } + + } while((totalWriteLength < outputProcessed) && !error); + + // STEP 7.5 + // + // We now have data in our input buffer that has already been compressed. + // We want to remove all the processed data from the input buffer, + // and we want to move any unprocessed data to the beginning of the buffer. + // + // If the amount processed is less than the valid buffer size, we have leftovers. + + NSUInteger inputRemaining = inputDataSize - inputProcessed; + if (inputRemaining > 0) + { + void *inputDst = [inputData mutableBytes]; + void *inputSrc = [inputData mutableBytes] + inputProcessed; + + memmove(inputDst, inputSrc, inputRemaining); + } + + inputDataSize = inputRemaining; + + // Are we done yet? + + done = ((flush == Z_FINISH) && (inputDataSize == 0)); + + // STEP 8 + // Loop repeats until end of data (or unlikely error) + + } // end @autoreleasepool + + } while (!done && error == nil); + + // STEP 9 + + [inputStream close]; + [outputStream close]; + + // STEP 10 + + deflateEnd(&strm); + + // We're done! + // Report success or failure back to the logging thread/queue. + + if (error) + { + // Remove output file. + // Our compression attempt failed. + + NSLogError(@"Compression of %@ failed: %@", inputFilePath, error); + error = nil; + BOOL ok = [[NSFileManager defaultManager] removeItemAtPath:tempOutputFilePath error:&error]; + if (!ok) + NSLogError(@"Failed to clean up %@ after failed compression: %@", tempOutputFilePath, error); + + // Report failure to class via logging thread/queue + + dispatch_async([DDLog loggingQueue], ^{ @autoreleasepool { + [self compressionDidFail:logFile]; + }}); + } + else + { + // Remove original input file. + // It will be replaced with the new compressed version. + + error = nil; + BOOL ok = [[NSFileManager defaultManager] removeItemAtPath:inputFilePath error:&error]; + if (!ok) + NSLogWarn(@"Warning: failed to remove original file %@ after compression: %@", inputFilePath, error); + + // Mark the compressed file as archived, + // and then move it into its final destination. + // + // temp-log-ABC123.txt.gz -> log-ABC123.txt.gz + // + // The reason we were using the "temp-" prefix was so the file would not be + // considered a log file while it was only partially complete. + // Only files that begin with "log-" are considered log files. + + DDLogFileInfo *compressedLogFile = [DDLogFileInfo logFileWithPath:tempOutputFilePath]; + compressedLogFile.isArchived = YES; + + NSString *outputFileName = [logFile fileNameByAppendingPathExtension:@"gz"]; + [compressedLogFile renameFile:outputFileName]; + + // Report success to class via logging thread/queue + + dispatch_async([DDLog loggingQueue], ^{ @autoreleasepool { + [self compressionDidSucceed:compressedLogFile]; + }}); + } + + } // end @autoreleasepool +} + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDLogFileInfo (Compressor) + +@dynamic isCompressed; + +- (BOOL)isCompressed +{ + return [[[self fileName] pathExtension] isEqualToString:@"gz"]; +} + +- (NSString *)tempFilePathByAppendingPathExtension:(NSString *)newExt +{ + // Example: + // + // Current File Name: "/full/path/to/log-ABC123.txt" + // + // newExt: "gzip" + // result: "/full/path/to/temp-log-ABC123.txt.gzip" + + NSString *tempFileName = [NSString stringWithFormat:@"temp-%@", [self fileName]]; + + NSString *newFileName = [tempFileName stringByAppendingPathExtension:newExt]; + + NSString *fileDir = [[self filePath] stringByDeletingLastPathComponent]; + + NSString *newFilePath = [fileDir stringByAppendingPathComponent:newFileName]; + + return newFilePath; +} + +- (NSString *)fileNameByAppendingPathExtension:(NSString *)newExt +{ + // Example: + // + // Current File Name: "log-ABC123.txt" + // + // newExt: "gzip" + // result: "log-ABC123.txt.gzip" + + NSString *fileNameExtension = [[self fileName] pathExtension]; + + if ([fileNameExtension isEqualToString:newExt]) + { + return [self fileName]; + } + + return [[self fileName] stringByAppendingPathExtension:newExt]; +} + +@end diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index 6fec75937..a0f6429a2 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -378,7 +378,7 @@ - (void)receivedOrphanBlock { - (void)sendRequest:(DSMessageRequest *)request { NSString *type = [request type]; NSData *payload = [request toData]; - DSLog(@"%@:%u sendRequest: [%@]: %@", self.host, self.port, type, [payload hexString]); +// DSLog(@"%@:%u sendRequest: [%@]: %@", self.host, self.port, type, [payload hexString]); [self sendMessage:payload type:type]; } From 9b57d27c541e1052eb09774c80a7f71a623c7fc4 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 18 Dec 2024 14:35:40 +0700 Subject: [PATCH 076/133] fix: cache dashAmount and direction in DSTransaction --- .../Models/Transactions/Base/DSTransaction.h | 7 +- .../Models/Transactions/Base/DSTransaction.m | 68 ++++++++++++++----- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.h b/DashSync/shared/Models/Transactions/Base/DSTransaction.h index e5a0dee4a..67fca128b 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.h +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.h @@ -90,7 +90,8 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) @property (nonatomic, assign) uint32_t lockTime; @property (nonatomic, assign) uint64_t feeUsed; @property (nonatomic, assign) uint64_t roundedFeeCostPerByte; -@property (nonatomic, readonly) uint64_t amountSent; +@property (nonatomic, readonly) DSTransactionDirection direction; +@property (nonatomic, readonly) uint64_t dashAmount; @property (nonatomic, readonly) NSData *payloadData; @property (nonatomic, readonly) NSData *payloadDataForHash; @property (nonatomic, assign) uint32_t payloadOffset; @@ -177,8 +178,4 @@ typedef NS_ENUM(NSInteger, DSTransactionSortType) @end -@interface DSTransaction (Extensions) -- (DSTransactionDirection)direction; -@end - NS_ASSUME_NONNULL_END diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index bdc9d8966..b4c99477e 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -58,6 +58,8 @@ @interface DSTransaction () @property (nonatomic, strong) NSSet *destinationBlockchainIdentities; @property (nonatomic, strong) NSMutableArray *mInputs; @property (nonatomic, strong) NSMutableArray *mOutputs; +@property (nonatomic, assign) DSTransactionDirection cachedDirection; +@property (nonatomic, assign) uint64_t cachedDashAmount; @end @@ -96,6 +98,8 @@ - (instancetype)initOnChain:(DSChain *)chain { self.blockHeight = TX_UNCONFIRMED; self.sourceBlockchainIdentities = [NSSet set]; self.destinationBlockchainIdentities = [NSSet set]; + self.cachedDirection = DSTransactionDirection_NotAccountFunds; + self.cachedDashAmount = UINT64_MAX; return self; } @@ -319,23 +323,59 @@ - (NSString *)longDescription { self.outputs]; } -// retuns the amount sent from the wallet by the trasaction (total wallet outputs consumed, change and fee included) -- (uint64_t)amountSent { +- (uint64_t)dashAmount { + if (self.cachedDashAmount != UINT64_MAX) { + return self.cachedDashAmount; + } + uint64_t amount = 0; - for (DSTransactionInput *input in self.inputs) { - UInt256 hash = input.inputHash; - DSTransaction *tx = [self.chain transactionForHash:hash]; - DSAccount *account = [self.chain firstAccountThatCanContainTransaction:tx]; - uint32_t n = input.index; - if (n < tx.outputs.count) { - DSTransactionOutput *output = tx.outputs[n]; - if ([account containsAddress:output.address]) - amount += output.amount; + const uint64_t sent = [self.chain amountSentByTransaction:self]; + const uint64_t received = [self.chain amountReceivedFromTransaction:self]; + uint64_t fee = self.feeUsed; + + if (fee == UINT64_MAX) { + fee = 0; + } + + if (sent > 0 && (received + fee) == sent) { + // moved + amount = 0; + self.cachedDirection = DSTransactionDirection_Moved; + } else if (sent > 0) { + // sent + if (received > sent) { + // NOTE: During the sync we may get an incorrect amount + return UINT64_MAX; } + + self.cachedDirection = DSTransactionDirection_Sent; + amount = sent - received - fee; + } else if (received > 0) { + // received + self.cachedDirection = DSTransactionDirection_Received; + amount = received; + } else { + // no funds moved on this account + self.cachedDirection = DSTransactionDirection_NotAccountFunds; + amount = 0; } + + self.cachedDashAmount = amount; + return amount; } +- (DSTransactionDirection)direction { + if (self.cachedDirection != DSTransactionDirection_NotAccountFunds) { + return self.cachedDirection; + } + + DSTransactionDirection direction = [self.chain directionOfTransaction: self]; + self.cachedDirection = direction; + + return direction; +} + // size in bytes if signed, or estimated size assuming compact pubkey sigs - (size_t)size { @synchronized(self) { @@ -920,9 +960,3 @@ - (BOOL)saveInitialInContext:(NSManagedObjectContext *)context { } @end - -@implementation DSTransaction (Extensions) -- (DSTransactionDirection)direction { - return [_chain directionOfTransaction: self]; -} -@end From a064058f83f55e8dfd2b7e87e4f12a626ad1b8c3 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 18 Dec 2024 14:35:49 +0700 Subject: [PATCH 077/133] fix: sync fixes --- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 4 +++ .../Models/CoinJoin/DSTransaction+CoinJoin.m | 2 +- .../Models/CoinJoin/Utils/DSMasternodeGroup.m | 26 ++++++++++++------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 2acdacb1b..68179ce9f 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -45,6 +45,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isDenominatedAmount:(uint64_t)amount; - (BOOL)isFullyMixed:(DSUTXO)utxo; + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; ++ (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction account:(DSAccount *)account; - (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed; - (uint64_t)getSmallestDenomination; - (void)updateOptions:(CoinJoinClientOptions *)options; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index d313bfab1..70dffaabe 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -158,6 +158,10 @@ - (BOOL)isMixingFeeTx:(UInt256)txId { + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction { DSAccount *account = [transaction.chain firstAccountThatCanContainTransaction:transaction]; + return [DSCoinJoinWrapper coinJoinTxTypeForTransaction:transaction account:account]; +} + ++ (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction account:(DSAccount *)account { NSArray *amountsSent = [account amountsSentByTransaction:transaction]; Transaction *tx = [transaction ffi_malloc:transaction.chain.chainType]; diff --git a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m index 4cb3ae06c..59ce8af24 100644 --- a/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m +++ b/DashSync/shared/Models/CoinJoin/DSTransaction+CoinJoin.m @@ -65,7 +65,7 @@ - (DSTransaction *)initWithTransaction:(Transaction *)transaction onChain:(DSCha NSString *address = [DSKeyManager addressWithScriptPubKey:scriptPubKey forChain:chain]; NSNumber *amount = @(output->amount); - [addresses addObject:address ?: [NSNull null]]; // Use NSNull for OP_RETURN or similar + [addresses addObject:address ?: [NSNull null]]; // Use NSNull turned into OP_RETURN script later [amounts addObject:amount]; } diff --git a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m index c84054c5e..b169c49f3 100644 --- a/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m +++ b/DashSync/shared/Models/CoinJoin/Utils/DSMasternodeGroup.m @@ -38,7 +38,7 @@ @interface DSMasternodeGroup () @property (nonatomic, strong) DSChain *chain; @property (nonatomic, weak, nullable) DSCoinJoinManager *coinJoinManager; -@property (nonatomic, strong) NSMutableSet *pendingSessions; +@property (nonatomic, strong) NSMutableSet *mutablePendingSessions; @property (nonatomic, strong) NSMutableDictionary *masternodeMap; @property (nonatomic, strong) NSMutableDictionary *sessionMap; @property (nonatomic, strong) NSMutableDictionary *addressMap; @@ -62,7 +62,7 @@ - (instancetype)initWithManager:(DSCoinJoinManager *)manager { if (self) { _coinJoinManager = manager; _chain = manager.chain; - _pendingSessions = [NSMutableSet set]; + _mutablePendingSessions = [NSMutableSet set]; _mutablePendingClosingMasternodes = [NSMutableArray array]; _masternodeMap = [NSMutableDictionary dictionary]; _sessionMap = [NSMutableDictionary dictionary]; @@ -181,6 +181,12 @@ - (NSArray *)pendingClosingMasternodes { } } +- (NSSet *)pendingSessions { + @synchronized(self.mutablePendingSessions) { + return [self.mutablePendingSessions copy]; + } +} + - (void)triggerConnections { [self triggerConnectionsJobWithDelay:0]; } @@ -238,14 +244,13 @@ - (void)triggerConnectionsJobWithDelay:(NSTimeInterval)delay { } - (DSPeer *)getNextPendingMasternode { - NSArray *pendingSessionsCopy = [self.pendingSessions copy]; NSArray *pendingClosingMasternodesCopy = self.pendingClosingMasternodes; DSPeer *peerWithLeastBackoff = nil; NSValue *sessionValueWithLeastBackoff = nil; UInt256 sessionId = UINT256_ZERO; NSDate *leastBackoffTime = [NSDate distantFuture]; - for (NSValue *sessionValue in pendingSessionsCopy) { + for (NSValue *sessionValue in self.pendingSessions) { [sessionValue getValue:&sessionId]; DSSimplifiedMasternodeEntry *mixingMasternodeInfo = [self mixingMasternodeAddressFor:sessionId]; @@ -300,10 +305,10 @@ - (DSSimplifiedMasternodeEntry *)mixingMasternodeAddressFor:(UInt256)sessionId { } - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)sessionId { - @synchronized (self.pendingSessions) { + @synchronized (self.mutablePendingSessions) { DSLog(@"[%@] CoinJoin: adding masternode for mixing. maxConnections = %lu, protx: %@, sessionId: %@", self.chain.name, (unsigned long)_maxConnections, uint256_hex(proTxHash), uint256_hex(sessionId)); NSValue *sessionIdValue = [NSValue valueWithBytes:&sessionId objCType:@encode(UInt256)]; - [self.pendingSessions addObject:sessionIdValue]; + [self.mutablePendingSessions addObject:sessionIdValue]; NSValue *proTxHashKey = [NSValue value:&proTxHash withObjCType:@encode(UInt256)]; [self.masternodeMap setObject:sessionIdValue forKey:proTxHashKey]; @@ -317,7 +322,7 @@ - (BOOL)addPendingMasternode:(UInt256)proTxHash clientSessionId:(UInt256)session } - (void)updateMaxConnections { - _maxConnections = self.pendingSessions.count; + _maxConnections = self.mutablePendingSessions.count; NSUInteger connections = MIN(self.maxConnections, self.coinJoinManager.options->coinjoin_sessions); DSLog(@"[%@] CoinJoin: updating max connections to min(%lu, %lu)", self.chain.name, (unsigned long)_maxConnections, (unsigned long)self.coinJoinManager.options->coinjoin_sessions); @@ -349,11 +354,12 @@ - (void)updateMaxConnections:(NSUInteger)connections { - (void)checkMasternodesWithoutSessions { NSMutableArray *masternodesToDrop = [NSMutableArray array]; + NSArray *pendingSessions = self.pendingSessions; for (DSPeer *peer in self.connectedPeers) { BOOL found = false; - for (NSValue *value in self.pendingSessions) { + for (NSValue *value in pendingSessions) { UInt256 sessionId; [value getValue:&sessionId]; DSSimplifiedMasternodeEntry *mixingMasternodeAddress = [self mixingMasternodeAddressFor:sessionId]; @@ -533,9 +539,9 @@ - (void)peer:(nonnull DSPeer *)peer disconnectedWithError:(nonnull NSError *)err NSValue *proTxHashKey = [NSValue valueWithBytes:&proTxHash objCType:@encode(UInt256)]; NSValue *sessionIdObject = [self.masternodeMap objectForKey:proTxHashKey]; - @synchronized (self.pendingSessions) { + @synchronized (self.mutablePendingSessions) { if (sessionIdObject) { - [self.pendingSessions removeObject:sessionIdObject]; + [self.mutablePendingSessions removeObject:sessionIdObject]; [self.sessionMap removeObjectForKey:sessionIdObject]; } From 9e286c7aa089ef48a7697c88e7e67a1427403048 Mon Sep 17 00:00:00 2001 From: pankcuf Date: Fri, 20 Dec 2024 06:56:04 +0700 Subject: [PATCH 078/133] fix: asset lock payload --- .../Base/DSAssetLockTransaction.m | 31 +++++++++++++++++++ Example/Podfile.lock | 4 +-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m index 1ce845e2a..e0f08f013 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m @@ -19,6 +19,7 @@ #import "DSChain.h" #import "DSTransactionFactory.h" #import "NSData+Dash.h" +#import "NSMutableData+Dash.h" @implementation DSAssetLockTransaction @@ -58,4 +59,34 @@ - (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; +} + @end diff --git a/Example/Podfile.lock b/Example/Podfile.lock index c12111213..467fce614 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -657,7 +657,7 @@ PODS: - gRPC/Interface-Legacy (1.49.0): - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) - - Protobuf (3.28.3) + - Protobuf (3.29.2) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -721,7 +721,7 @@ SPEC CHECKSUMS: gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: 5a8a7781d8e1004302f108977ac2d5b99323146f + Protobuf: ba5d83b2201386fec27d484c099cac510ea5c169 SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 From dbf4a279a9ced31daf08f1cb5aee172786a84766 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 22 Dec 2024 15:53:20 +0700 Subject: [PATCH 079/133] fix: add dest_change to coin control --- DashSync/shared/Models/CoinJoin/DSCoinControl.h | 3 ++- DashSync/shared/Models/CoinJoin/DSCoinControl.m | 8 +++++++- DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m | 4 ++-- .../shared/Models/Transactions/Base/DSTransaction.m | 2 +- DashSync/shared/Models/Wallet/DSAccount.m | 13 +++++++++++-- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.h b/DashSync/shared/Models/CoinJoin/DSCoinControl.h index 5e75429fa..a2fbb0af5 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.h @@ -37,8 +37,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) NSNumber *confirmTarget; @property (nonatomic, assign) CoinType coinType; @property (nonatomic, strong) NSMutableOrderedSet *setSelected; +@property (nonatomic, strong) NSString *destChange; -- (instancetype)initWithFFICoinControl:(CoinControl *)coinControl; +- (instancetype)initWithFFICoinControl:(CoinControl *)coinControl chainType:(ChainType)chainType; - (BOOL)hasSelected; - (BOOL)isSelected:(DSUTXO)utxo; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinControl.m b/DashSync/shared/Models/CoinJoin/DSCoinControl.m index 48cf94395..f410679be 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinControl.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinControl.m @@ -19,13 +19,18 @@ @implementation DSCoinControl -- (instancetype)initWithFFICoinControl:(CoinControl *)coinControl { +- (instancetype)initWithFFICoinControl:(CoinControl *)coinControl chainType:(ChainType)chainType { if (!(self = [super init])) return nil; self.coinType = coinControl->coin_type; self.minDepth = coinControl->min_depth; self.maxDepth = coinControl->max_depth; self.avoidAddressReuse = coinControl->avoid_address_reuse; self.allowOtherInputs = coinControl->allow_other_inputs; + + if (coinControl->dest_change) { + char *c_string = address_with_script_pubkey(coinControl->dest_change->ptr, coinControl->dest_change->len, chainType); + self.destChange = [NSString stringWithUTF8String:c_string]; + } if (coinControl->set_selected && coinControl->set_selected_size > 0) { self.setSelected = [[NSMutableOrderedSet alloc] init]; @@ -59,6 +64,7 @@ - (instancetype)init { _avoidPartialSpends = NO; _avoidAddressReuse = NO; _minDepth = 0; + _destChange = NULL; } return self; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 70dffaabe..7a8fc3bfd 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -347,7 +347,7 @@ bool isMineInput(uint8_t (*tx_hash)[32], uint32_t index, const void *context) { @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); ChainType chainType = wrapper.chain.chainType; - DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; + DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl chainType:wrapper.chain.chainType]; NSArray *coins = [wrapper.manager availableCoins:walletEx onlySafe:onlySafe coinControl:cc minimumAmount:1 maximumAmount:MAX_MONEY minimumSumAmount:MAX_MONEY maximumCount:0]; gatheredOutputs = malloc(sizeof(GatheredOutputs)); @@ -471,7 +471,7 @@ bool commitTransaction(struct Recipient **items, uintptr_t item_count, CoinContr @synchronized (context) { DSCoinJoinWrapper *wrapper = AS_OBJC(context); - DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl]; + DSCoinControl *cc = [[DSCoinControl alloc] initWithFFICoinControl:coinControl chainType:wrapper.chain.chainType]; result = [wrapper.manager commitTransactionForAmounts:amounts outputs:scripts coinControl:cc onPublished:^(UInt256 txId, NSError * _Nullable error) { @synchronized (context) { if (error) { diff --git a/DashSync/shared/Models/Transactions/Base/DSTransaction.m b/DashSync/shared/Models/Transactions/Base/DSTransaction.m index b4c99477e..23480fe63 100644 --- a/DashSync/shared/Models/Transactions/Base/DSTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSTransaction.m @@ -327,7 +327,7 @@ - (uint64_t)dashAmount { if (self.cachedDashAmount != UINT64_MAX) { return self.cachedDashAmount; } - + uint64_t amount = 0; const uint64_t sent = [self.chain amountSentByTransaction:self]; const uint64_t received = [self.chain amountReceivedFromTransaction:self]; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 5e4835587..6243034c1 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -1192,9 +1192,18 @@ - (DSTransaction *)updateTransaction:(DSTransaction *)transaction if (followBIP69sorting) { [transaction sortInputsAccordingToBIP69]; } - + if (balance - (amount + feeAmount) >= self.wallet.chain.minOutputAmount) { - [transaction addOutputAddress:self.changeAddress amount:balance - (amount + feeAmount)]; + NSString *changeAddress; + + if (coinControl.destChange) { + changeAddress = coinControl.destChange; + } else { + changeAddress = self.changeAddress; + } + + [transaction addOutputAddress:changeAddress amount:balance - (amount + feeAmount)]; + if (followBIP69sorting) { [transaction sortOutputsAccordingToBIP69]; } else if (sortType == DSTransactionSortType_Shuffle) { From a7e815fb2ea259f66db50104b988ced13dba46bf Mon Sep 17 00:00:00 2001 From: pankcuf Date: Fri, 10 Jan 2025 20:26:56 +0800 Subject: [PATCH 080/133] 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 @@ - + - + - + - + - + - + - + - + - + - +