From 780a38497567717bc0408e2f29fc4eea44864dce Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 4 Jan 2024 17:37:04 +0700 Subject: [PATCH 01/95] 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 11/95] 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 12/95] 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 13/95] 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 14/95] 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 15/95] 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 16/95] 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 17/95] 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 18/95] 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 19/95] 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 20/95] 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 21/95] 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 22/95] 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 23/95] 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 24/95] 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 25/95] 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 26/95] 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 27/95] 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 28/95] 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 29/95] 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 30/95] 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 31/95] 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 32/95] 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 33/95] 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 34/95] 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 35/95] 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 36/95] 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 37/95] 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 38/95] 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 39/95] 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 40/95] 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 41/95] 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 42/95] 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 43/95] 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 44/95] 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 45/95] 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 46/95] 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 47/95] 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 48/95] 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 49/95] 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 50/95] 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 51/95] 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 52/95] 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 53/95] 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 54/95] 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 55/95] 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 56/95] 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 57/95] 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 58/95] 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 59/95] 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 60/95] 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 61/95] 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 62/95] 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 63/95] 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 64/95] 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 65/95] 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 66/95] 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 67/95] 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 68/95] 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 69/95] 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 70/95] 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 71/95] 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 72/95] 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 73/95] 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 74/95] 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 75/95] 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 76/95] 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 77/95] 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 78/95] 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 79/95] 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 dfc86c645bb3f2a0e88ab026bfda1b3628a8a2be Mon Sep 17 00:00:00 2001 From: pankcuf Date: Fri, 20 Dec 2024 06:56:04 +0700 Subject: [PATCH 80/95] 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 ffbfc1900..7c01d39f9 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.28.3) + - Protobuf (3.29.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: 5a8a7781d8e1004302f108977ac2d5b99323146f + Protobuf: ba5d83b2201386fec27d484c099cac510ea5c169 SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 From d92d814f3f9a3d40126a9359b578831d8eb97293 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 21 Jan 2025 22:12:34 +0700 Subject: [PATCH 81/95] fix: asset lock tx change --- .../shared/Models/Transactions/Base/DSAssetLockTransaction.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m index e0f08f013..eb0cd48d5 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m @@ -79,7 +79,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; From 60ac6c26f8391fed4e59d07b6439642976e45d88 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 31 Jan 2025 12:30:27 +0800 Subject: [PATCH 82/95] fix: memory & lock issues in sortTransactions --- DashSync/shared/Models/Wallet/DSAccount.h | 3 --- DashSync/shared/Models/Wallet/DSAccount.m | 29 ++++++----------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/DashSync/shared/Models/Wallet/DSAccount.h b/DashSync/shared/Models/Wallet/DSAccount.h index cb261c2f4..dc0ace3c8 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.h +++ b/DashSync/shared/Models/Wallet/DSAccount.h @@ -289,9 +289,6 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSAccountNewAccountShouldBeAddedFromT // returns the fee for the given transaction if all its inputs are from wallet transactions, UINT64_MAX otherwise - (uint64_t)feeForTransaction:(DSTransaction *)transaction; -// historical wallet balance after the given transaction, or current balance if transaction is not registered in wallet -- (uint64_t)balanceAfterTransaction:(DSTransaction *)transaction; - - (void)chainUpdatedBlockHeight:(int32_t)height; - (NSArray *)setBlockHeight:(int32_t)height andTimestamp:(NSTimeInterval)timestamp forTransactionHashes:(NSArray *)txHashes; diff --git a/DashSync/shared/Models/Wallet/DSAccount.m b/DashSync/shared/Models/Wallet/DSAccount.m index 6243034c1..d994a0554 100644 --- a/DashSync/shared/Models/Wallet/DSAccount.m +++ b/DashSync/shared/Models/Wallet/DSAccount.m @@ -671,7 +671,6 @@ - (void)updateBalance { NSMutableOrderedSet *utxos = [NSMutableOrderedSet orderedSet]; NSMutableSet *spentOutputs = [NSMutableSet set], *invalidTx = [NSMutableSet set], *pendingTransactionHashes = [NSMutableSet set]; NSMutableDictionary *pendingCoinbaseLockedTransactionHashes = [NSMutableDictionary dictionary]; - NSMutableArray *balanceHistory = [NSMutableArray array]; uint32_t now = [NSDate timeIntervalSince1970]; for (DSFundsDerivationPath *derivationPath in self.fundDerivationPaths) { @@ -701,7 +700,6 @@ - (void)updateBalance { if (tx.blockHeight == TX_UNCONFIRMED && ([spent intersectsSet:spentOutputs] || [inputs intersectsSet:invalidTx])) { [invalidTx addObject:uint256_obj(tx.txHash)]; - [balanceHistory insertObject:@(balance) atIndex:0]; continue; } } else { @@ -754,7 +752,6 @@ - (void)updateBalance { if (pending || [inputs intersectsSet:pendingTransactionHashes]) { [pendingTransactionHashes addObject:uint256_obj(tx.txHash)]; - [balanceHistory insertObject:@(balance) atIndex:0]; continue; } @@ -765,7 +762,6 @@ - (void)updateBalance { pendingCoinbaseLockedTransactionHashes[@(lockedBlockHeight)] = [NSMutableSet set]; } [((NSMutableSet *)pendingCoinbaseLockedTransactionHashes[@(lockedBlockHeight)]) addObject:uint256_obj(tx.txHash)]; - [balanceHistory insertObject:@(balance) atIndex:0]; continue; } @@ -811,7 +807,6 @@ - (void)updateBalance { if (prevBalance < balance) totalReceived += balance - prevBalance; if (balance < prevBalance) totalSent += prevBalance - balance; - [balanceHistory insertObject:@(balance) atIndex:0]; prevBalance = balance; #if LOG_BALANCE_UPDATE DSLog(@"===UTXOS==="); @@ -835,7 +830,6 @@ - (void)updateBalance { self.pendingCoinbaseLockedTransactionHashes = pendingCoinbaseLockedTransactionHashes; self.spentOutputs = spentOutputs; self.utxos = utxos; - self.balanceHistory = balanceHistory; _totalSent = totalSent; _totalReceived = totalReceived; @@ -849,16 +843,6 @@ - (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; -} - - - (void)postBalanceDidChangeNotification { [[NSNotificationCenter defaultCenter] postNotificationName:DSWalletBalanceDidChangeNotification object:nil]; } @@ -895,21 +879,22 @@ __block __weak BOOL (^_isAscending)(id, id) = isAscending = ^BOOL(DSTransaction }] != 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) { - if (_isAscending(self.allTx[uint256_obj(input.inputHash)], tx2)) return YES; - } + return NO; }; + NSArray *externalAddresses = self.externalAddresses; + NSArray *internalAddresses = self.internalAddresses; + [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); + NSUInteger i = transactionAddressIndex(tx1, internalAddresses); + NSUInteger j = transactionAddressIndex(tx2, (i == NSNotFound) ? externalAddresses : internalAddresses); - if (i == NSNotFound && j != NSNotFound) i = transactionAddressIndex(tx1, self.externalAddresses); + if (i == NSNotFound && j != NSNotFound) i = transactionAddressIndex(tx1, externalAddresses); if (i == NSNotFound || j == NSNotFound || i == j) return NSOrderedSame; return (i > j) ? NSOrderedAscending : NSOrderedDescending; }]; From fb036d99872cc14300b0c7d7660bf62e28e29071 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 6 Feb 2025 13:54:54 +0700 Subject: [PATCH 83/95] fix: notify UI of double spent or unconfirmed tx --- .../DSFriendRequestEntity+CoreDataClass.m | 2 +- .../Chain Managers/DSTransactionManager.m | 35 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m index 65cb2091c..794c36d13 100644 --- a/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSFriendRequestEntity+CoreDataClass.m @@ -88,7 +88,7 @@ - (void)sendAmount:(uint64_t)amount fromAccount:(DSAccount *)account requestingA NSAssert([paymentRequest isValidAsNonDashpayPaymentRequest], @"Payment request must be valid"); - // TODO: mixed only? + // TODO: MOCK_DASHPAY mixed only? [account.wallet.chain.chainManager.transactionManager confirmPaymentRequest:paymentRequest usingUserBlockchainIdentity:nil fromAccount:account diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index 1ca2fadc1..da0e5a76d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -213,6 +213,13 @@ - (void)publishTransaction:(DSTransaction *)transaction completion:(void (^)(NSE dispatch_async(self.chainManager.chain.networkingQueue, ^{ [self performSelector:@selector(txTimeout:) withObject:hash afterDelay:PROTOCOL_TIMEOUT]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification + object:nil + userInfo:@{DSChainManagerNotificationChainKey: self.chain, + DSTransactionManagerNotificationTransactionKey: transaction}]; + }); for (DSPeer *p in peers) { if (p.status != DSPeerStatus_Connected) continue; @@ -280,7 +287,11 @@ - (void)removeUnrelayedTransactionsFromPeer:(DSPeer *)peer { for (DSTransaction *transaction in transactionsSet) { if (transaction.blockHeight != TX_UNCONFIRMED) continue; hash = uint256_obj(transaction.txHash); +#if DEBUG + DSLogPrivate(@"[%@] checking published callback %@ -> %@", self.chain.name, uint256_reverse_hex(transaction.txHash), self.publishedCallback[hash] ? @"OK" : @"no callback"); +#else DSLog(@"[%@] checking published callback -> %@", self.chain.name, self.publishedCallback[hash] ? @"OK" : @"no callback"); +#endif if (self.publishedCallback[hash] != NULL) continue; DSLog(@"[%@] transaction relays count %lu, transaction requests count %lu", self.chain.name, (unsigned long)[self.txRelays[hash] count], (unsigned long)[self.txRequests[hash] count]); DSAccount *account = [self.chain firstAccountThatCanContainTransaction:transaction]; @@ -292,6 +303,9 @@ - (void)removeUnrelayedTransactionsFromPeer:(DSPeer *)peer { NSAssert(FALSE, @"This probably needs more implementation work, if you are here now is the time to do it."); continue; } + + BOOL updateTransaction = NO; + if ([self.txRelays[hash] count] == 0 && [self.txRequests[hash] count] == 0) { // if this is for a transaction we sent, and it wasn't already known to be invalid, notify user of failure if (!rescan && account && [account amountSentByTransaction:transaction] > 0 && [account transactionIsValid:transaction]) { @@ -326,7 +340,7 @@ - (void)removeUnrelayedTransactionsFromPeer:(DSPeer *)peer { DSLog(@"[%@] removing transaction ", self.chain.name); #endif [transactionsToBeRemoved addObject:transaction]; - + updateTransaction = YES; } else if ([self.txRelays[hash] count] < self.peerManager.maxConnectCount) { // set timestamp 0 to mark as unverified #if DEBUG @@ -337,6 +351,17 @@ - (void)removeUnrelayedTransactionsFromPeer:(DSPeer *)peer { [self.chain setBlockHeight:TX_UNCONFIRMED andTimestamp:0 forTransactionHashes:@[hash]]; + updateTransaction = YES; + } + + if (updateTransaction) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification + object:nil + userInfo:@{DSChainManagerNotificationChainKey: self.chain, + DSTransactionManagerNotificationTransactionKey: transaction, + DSTransactionManagerNotificationTransactionChangesKey: @{DSTransactionManagerNotificationTransactionAcceptedStatusKey: @(NO)}}]; + }); } } @@ -1113,6 +1138,14 @@ - (DSTransaction *)peer:(DSPeer *)peer requestedTransaction:(UInt256)txHash { if (callback && !isTransactionValid) { [self.publishedTx removeObjectForKey:hash]; error = [NSError errorWithCode:401 localizedDescriptionKey:@"Double spend"]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification + object:nil + userInfo:@{DSChainManagerNotificationChainKey: self.chain, + DSTransactionManagerNotificationTransactionKey: transaction, + DSTransactionManagerNotificationTransactionChangesKey: @{DSTransactionManagerNotificationTransactionAcceptedStatusKey: @(NO)}}]; + }); } else if (transaction) { for (DSAccount *account in accounts) { if (![account transactionForHash:txHash]) { From c2c571e0ce6d7594978ec624515309f05ad04401 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 11 Feb 2025 19:30:09 +0700 Subject: [PATCH 84/95] fix: polymorphism in DSAssetLockTransaction --- .../shared/Models/Transactions/Base/DSAssetLockTransaction.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m index eb0cd48d5..e9aca1356 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSAssetLockTransaction.m @@ -77,14 +77,15 @@ - (NSData *)basePayloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:NO] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; } } + - (size_t)size { return [super size] + [self payloadData].length; } From d077de5240257ef9dc6f442f0486ddaa67eac125 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 11 Feb 2025 21:25:12 +0700 Subject: [PATCH 85/95] fix: polymorphism in DSAssetUnlockTransaction --- .../Models/Transactions/Base/DSAssetUnlockTransaction.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m b/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m index c6b77e254..f6d8ee8fc 100644 --- a/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m +++ b/DashSync/shared/Models/Transactions/Base/DSAssetUnlockTransaction.m @@ -80,9 +80,9 @@ - (NSData *)basePayloadData { return data; } -- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex { +- (NSData *)toDataWithSubscriptIndex:(NSUInteger)subscriptIndex anyoneCanPay:(BOOL)anyoneCanPay { @synchronized(self) { - NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:NO] mutableCopy]; + NSMutableData *data = [[super toDataWithSubscriptIndex:subscriptIndex anyoneCanPay:anyoneCanPay] mutableCopy]; [data appendCountedData:[self payloadData]]; if (subscriptIndex != NSNotFound) [data appendUInt32:SIGHASH_ALL]; return data; From 463eb053303c0932804c2715987ae106730ecb0d Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Sun, 16 Feb 2025 14:55:49 +0700 Subject: [PATCH 86/95] fix: check for valid data in ecdsaKeyAddressFromPublicKeyData --- .../shared/Models/Managers/Chain Managers/DSKeyManager.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m index de71bac40..982cd11ce 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSKeyManager.m @@ -192,10 +192,13 @@ + (UInt160)ecdsaKeyPublicKeyHashFromSecret:(NSString *)secret forChainType:(Chai } + (NSString *_Nullable)ecdsaKeyAddressFromPublicKeyData:(NSData *)data forChainType:(ChainType)chainType { + if (!data || data.length == 0) { + return nil; + } + return [DSKeyManager NSStringFrom:ecdsa_address_from_public_key_data(data.bytes, data.length, chainType)]; } - - (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]; From 707eae91e7caa713f774e609f46c6871d5914a62 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 17 Feb 2025 14:02:17 +0700 Subject: [PATCH 87/95] fix: remove unnesesary synchronizations --- .../shared/Models/CoinJoin/DSCoinJoinWrapper.m | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index 7a8fc3bfd..a2206c636 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -97,9 +97,7 @@ - (void)doMaintenance { } - (BOOL)isDenominatedAmount:(uint64_t)amount { - @synchronized (self) { - return is_denominated_amount(amount); - } + return is_denominated_amount(amount); } - (BOOL)isFullyMixed:(DSUTXO)utxo { @@ -203,15 +201,11 @@ - (uint64_t)getSmallestDenomination { } - (uint64_t)getCollateralAmount { - @synchronized (self) { - return get_collateral_amount(); - } + return get_collateral_amount(); } - (uint64_t)getMaxCollateralAmount { - @synchronized (self) { - return get_max_collateral_amount(); - } + return get_max_collateral_amount(); } - (BOOL)hasCollateralInputs:(BOOL)onlyConfirmed { @@ -221,9 +215,7 @@ - (BOOL)hasCollateralInputs:(BOOL)onlyConfirmed { } - (uint32_t)amountToDenomination:(uint64_t)amount { - @synchronized (self) { - return amount_to_denomination(amount); - } + return amount_to_denomination(amount); } - (int32_t)getRealOutpointCoinJoinRounds:(DSUTXO)utxo { From 212b8a5f33bcc4d57b31c0713436c1d0b41f0741 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 28 Feb 2025 15:55:30 +0700 Subject: [PATCH 88/95] fix: process DSTX --- DashSync/shared/Models/Network/DSPeer.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index a0f6429a2..7db959267 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -1040,7 +1040,7 @@ - (void)acceptInvMessage:(NSData *)message { if (count == 0) { DSLogWithLocation(self, @"Got empty Inv message"); } - if (count > 0 && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodePing) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodePaymentVote) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodeVerify) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_GovernanceObjectVote) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_DSTx)) { + if (count > 0 && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodePing) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodePaymentVote) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_MasternodeVerify) && ([message UInt32AtOffset:l.unsignedIntegerValue] != DSInvType_GovernanceObjectVote)) { DSLogWithLocation(self, @"got inv with %u item%@ (first item %@ with hash %@/%@)", (int)count, count == 1 ? @"" : @"s", [self nameOfInvMessage:[message UInt32AtOffset:l.unsignedIntegerValue]], [NSData dataWithUInt256:[message UInt256AtOffset:l.unsignedIntegerValue + sizeof(uint32_t)]].hexString, [NSData dataWithUInt256:[message UInt256AtOffset:l.unsignedIntegerValue + sizeof(uint32_t)]].reverse.hexString); } #endif @@ -1063,7 +1063,7 @@ - (void)acceptInvMessage:(NSData *)message { switch (type) { case DSInvType_Tx: [txHashes addObject:uint256_obj(hash)]; break; case DSInvType_TxLockRequest: [txHashes addObject:uint256_obj(hash)]; break; - case DSInvType_DSTx: break; + case DSInvType_DSTx: [txHashes addObject:uint256_obj(hash)]; break; case DSInvType_TxLockVote: break; case DSInvType_InstantSendDeterministicLock: [instantSendLockDHashes addObject:uint256_obj(hash)]; break; case DSInvType_InstantSendLock: [instantSendLockHashes addObject:uint256_obj(hash)]; break; From fd058258f5673d96e864ed5df5d69a40c49e988e Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 28 Feb 2025 15:55:48 +0700 Subject: [PATCH 89/95] feat: unlock coins from recently arrived mixing tx --- DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m | 1 + DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h | 1 + DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m | 12 +++++++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index fe2d60dfa..d526cae0d 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -256,6 +256,7 @@ - (void)handleTransactionReceivedNotification { #else DSLog(@"[%@] CoinJoin tx: Mixing Transaction: %@", self.chain.name, @""); #endif + [self.wrapper unlockOutputs:lastTransaction]; [self onTransactionProcessed:lastTransaction.txHash type:CoinJoinTransactionType_Mixing]; } } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 68179ce9f..2a9cfebbc 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -46,6 +46,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)isFullyMixed:(DSUTXO)utxo; + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction account:(DSAccount *)account; +- (void)unlockOutputs:(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 7a8fc3bfd..e8825d757 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -145,14 +145,14 @@ - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSStri - (void)notifyNewBestBlock:(DSBlock *)block { if (block) { @synchronized (self) { - notify_new_best_block(_clientManager, (uint8_t (*)[32])(block.blockHash.u8), block.height); + notify_new_best_block(self.clientManager, (uint8_t (*)[32])(block.blockHash.u8), block.height); } } } - (BOOL)isMixingFeeTx:(UInt256)txId { @synchronized (self) { - return is_mixing_fee_tx(_clientManager, (uint8_t (*)[32])(txId.u8)); + return is_mixing_fee_tx(self.clientManager, (uint8_t (*)[32])(txId.u8)); } } @@ -178,9 +178,15 @@ + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transac return type; } +- (void)unlockOutputs:(DSTransaction *)transaction { + Transaction *tx = [transaction ffi_malloc:transaction.chain.chainType]; + unlock_outputs(self.clientManager, tx); + [DSTransaction ffi_free:tx]; +} + - (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed { @synchronized (self) { - return get_anonymizable_balance(_clientManager, skipDenominated, skipUnconfirmed); + return get_anonymizable_balance(self.clientManager, skipDenominated, skipUnconfirmed); } } From f2c0a1dc928d5ccecb7adf379a18344ed3fa0904 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Tue, 4 Mar 2025 17:22:03 +0700 Subject: [PATCH 90/95] feat: coinjoin finishing mode --- .../Models/CoinJoin/DSCoinJoinManager.h | 2 + .../Models/CoinJoin/DSCoinJoinManager.m | 46 +++++++++++++++---- .../Models/CoinJoin/DSCoinJoinWrapper.h | 1 + .../Models/CoinJoin/DSCoinJoinWrapper.m | 16 +++++-- 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h index ec5811450..88b9275da 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.h @@ -48,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) DSCoinJoinWrapper *wrapper; @property (nonatomic, readonly) BOOL isWaitingForNewBlock; @property (atomic) BOOL isMixing; +@property (atomic) BOOL isShuttingDown; @property (readonly) BOOL isChainSynced; + (instancetype)sharedInstanceForChain:(DSChain *)chain; @@ -90,6 +91,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)minimumAnonymizableBalanceWithCompletion:(void (^)(uint64_t balance))completion; - (void)updateOptionsWithAmount:(uint64_t)amount; - (void)updateOptionsWithEnabled:(BOOL)isEnabled; +- (void)initiateShutdown; // Events - (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; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m index d526cae0d..69e5cb99f 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinManager.m @@ -130,8 +130,19 @@ - (void)updateOptionsWithEnabled:(BOOL)isEnabled { } } +- (void)updateOptionsWithSessions:(int32_t)sessions { + if (self.options->coinjoin_sessions != sessions) { + self.options->coinjoin_sessions = sessions; + + 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(@"[%@] 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->enable_coinjoin = true; self.options->coinjoin_amount = amount; self.options->coinjoin_rounds = rounds; self.options->coinjoin_sessions = sessions; @@ -195,6 +206,7 @@ - (void)doMaintenance { - (BOOL)startMixing { self.isMixing = true; + self.isShuttingDown = false; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSyncStateDidChangeNotification:) name:DSChainManagerSyncStateDidChangeNotification @@ -202,6 +214,20 @@ - (BOOL)startMixing { return [self.wrapper startMixing]; } +- (void)initiateShutdown { + if (self.isMixing && !self.isShuttingDown) { + DSLog(@"[%@] CoinJoinManager initiated shutdown", self.chain.name); + self.isShuttingDown = true; + [self updateOptionsWithSessions:0]; + [self.wrapper initiateShutdown]; + + if (self.masternodeGroup != nil && self.masternodeGroup.isRunning) { + [self.chain.chainManager.peerManager shouldSendDsq:false]; + [self.masternodeGroup stopAsync]; + } + } +} + - (void)stop { if (self.isMixing) { DSLog(@"[%@] CoinJoinManager stopping", self.chain.name); @@ -211,6 +237,7 @@ - (void)stop { [self updateOptionsWithEnabled:NO]; [self.wrapper stopAndResetClientManager]; [self stopAsync]; + self.isShuttingDown = false; } } @@ -314,9 +341,15 @@ - (void)refreshUnusedKeys { } - (void)processMessageFrom:(DSPeer *)peer message:(NSData *)message type:(NSString *)type { + if (!self.isMixing) { + return; + } + dispatch_async(self.processingQueue, ^{ if ([type isEqualToString:MSG_COINJOIN_QUEUE]) { - [self.wrapper processDSQueueFrom:peer message:message]; + if (!self.isShuttingDown) { + [self.wrapper processDSQueueFrom:peer message:message]; + } } else { [self.wrapper processMessageFrom:peer message:message type:type]; } @@ -973,9 +1006,10 @@ - (void)onMixingStarted:(nonnull NSArray *)statuses { } - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInterrupted { - 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"); - + if (self.isShuttingDown) { + [self stop]; + } + PoolStatus returnStatus = PoolStatus_ErrNotEnoughFunds; BOOL isError = YES; @@ -991,10 +1025,6 @@ - (void)onMixingComplete:(nonnull NSArray *)statuses isInterrupted:(BOOL)isInter returnStatus = status; } } - - if (locked_coins_amount > 0) { - print_locked_coins(self.wrapper.clientManager); - } [self.managerDelegate mixingComplete:isError errorStatus:returnStatus isInterrupted:isInterrupted]; } diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h index 2a9cfebbc..af83e12d3 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.h @@ -44,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)refreshUnusedKeys; - (BOOL)isDenominatedAmount:(uint64_t)amount; - (BOOL)isFullyMixed:(DSUTXO)utxo; +- (void)initiateShutdown; + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction; + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transaction account:(DSAccount *)account; - (void)unlockOutputs:(DSTransaction *)transaction; diff --git a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m index e8825d757..0a50247a2 100644 --- a/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m +++ b/DashSync/shared/Models/CoinJoin/DSCoinJoinWrapper.m @@ -91,11 +91,17 @@ - (BOOL)doAutomaticDenominatingWithDryRun:(BOOL)dryRun { - (void)doMaintenance { @synchronized (self) { Balance *balance = [[self.manager getBalance] ffi_malloc]; - do_maintenance(_clientManager, *balance); + do_maintenance(self.clientManager, *balance); [DSCoinJoinBalance ffi_free:balance]; } } +- (void)initiateShutdown { + @synchronized (self) { + initiate_shutdown(self.clientManager); + } +} + - (BOOL)isDenominatedAmount:(uint64_t)amount { @synchronized (self) { return is_denominated_amount(amount); @@ -179,9 +185,11 @@ + (CoinJoinTransactionType)coinJoinTxTypeForTransaction:(DSTransaction *)transac } - (void)unlockOutputs:(DSTransaction *)transaction { - Transaction *tx = [transaction ffi_malloc:transaction.chain.chainType]; - unlock_outputs(self.clientManager, tx); - [DSTransaction ffi_free:tx]; + @synchronized (self) { + Transaction *tx = [transaction ffi_malloc:transaction.chain.chainType]; + unlock_outputs(self.clientManager, tx); + [DSTransaction ffi_free:tx]; + } } - (uint64_t)getAnonymizableBalance:(BOOL)skipDenominated skipUnconfirmed:(BOOL)skipUnconfirmed { From 0f9b5dcd87c9cab75d2919c1d536444ccd24dc34 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 6 Mar 2025 13:38:56 +0700 Subject: [PATCH 91/95] chore: restore and bump DashSharedCore version --- DashSync.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DashSync.podspec b/DashSync.podspec index 3e0c5e57e..786084ecb 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.19' + s.dependency 'DashSharedCore', '0.5.0' s.dependency 'CocoaLumberjack', '3.7.2' s.ios.dependency 'DWAlertController', '0.2.1' s.dependency 'DSDynamicOptions', '0.1.2' From f2adb2f148e81bbd98d077e92edf5e5751abd4ce Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 6 Mar 2025 13:46:32 +0700 Subject: [PATCH 92/95] chore: cleanup Example podfile --- Example/Podfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Example/Podfile b/Example/Podfile index 8e17951f9..575852f60 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -3,7 +3,6 @@ def common_pods # pod 'DashSharedCore', :git => 'https://github.com/dashpay/dash-shared-core.git', :commit => '7dda9489a1d23221032e2b050c31a317a6e95631' # pod 'DashSharedCore', :path => '../../dash-shared-core/' pod 'DashSync', :path => '../' - pod 'DashSharedCore', :path => '../../dash-shared-core/' pod 'SDWebImage', '5.14.3' pod 'CocoaImageHashing', :git => 'https://github.com/ameingast/cocoaimagehashing.git', :commit => 'ad01eee' end From a11fbef6ccb9b8dd9faa5cf63e47f11f5551e074 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 14 Mar 2025 20:35:59 +0700 Subject: [PATCH 93/95] fix: downgrade cocoapods for CI --- .github/workflows/build.yml | 6 +++++- .github/workflows/coverage.yml | 4 ++++ .github/workflows/e2eTestsTestnet.yml | 4 ++++ .github/workflows/lint.yml | 4 ++++ .github/workflows/network.yml | 4 ++++ .github/workflows/syncTestMainnet.yml | 4 ++++ .github/workflows/syncTestTestnet.yml | 4 ++++ .github/workflows/test.yml | 5 +++++ 8 files changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dac065ec1..e0ed49999 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,6 +44,10 @@ jobs: - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: '15.4' + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim - name: Build dependencies @@ -59,4 +63,4 @@ jobs: -scheme "DashSync-Example" \ -workspace "DashSync.xcworkspace" \ -destination "platform=$platform,name=iPhone 13" \ - CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED=NO \ No newline at end of file + CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED=NO diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 6504c8e63..71a1fd2be 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -43,6 +43,10 @@ jobs: ${{ runner.os }}-pods- - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Dependencies working-directory: ./dashsync/Example run: pod install --repo-update diff --git a/.github/workflows/e2eTestsTestnet.yml b/.github/workflows/e2eTestsTestnet.yml index debb99192..85d59ffd8 100644 --- a/.github/workflows/e2eTestsTestnet.yml +++ b/.github/workflows/e2eTestsTestnet.yml @@ -45,6 +45,10 @@ jobs: ${{ runner.os }}-pods- - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Dependencies working-directory: ./dashsync/Example run: pod install --repo-update diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2afc376d8..911ae0a85 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -43,6 +43,10 @@ jobs: key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} restore-keys: | ${{ runner.os }}-pods- + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim - name: Dependencies diff --git a/.github/workflows/network.yml b/.github/workflows/network.yml index 342edf1b3..18268c01a 100644 --- a/.github/workflows/network.yml +++ b/.github/workflows/network.yml @@ -44,6 +44,10 @@ jobs: run: cargo install cargo-lipo - name: Rustup add targets run: rustup target add x86_64-apple-darwin + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Build Dependencies working-directory: ./dashsync/Example run: pod install --repo-update diff --git a/.github/workflows/syncTestMainnet.yml b/.github/workflows/syncTestMainnet.yml index 54aa929e3..4f62f4c3f 100644 --- a/.github/workflows/syncTestMainnet.yml +++ b/.github/workflows/syncTestMainnet.yml @@ -44,6 +44,10 @@ jobs: ${{ runner.os }}-pods- - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Dependencies working-directory: ./dashsync/Example run: pod install --repo-update diff --git a/.github/workflows/syncTestTestnet.yml b/.github/workflows/syncTestTestnet.yml index d55703616..7deb6cab1 100644 --- a/.github/workflows/syncTestTestnet.yml +++ b/.github/workflows/syncTestTestnet.yml @@ -45,6 +45,10 @@ jobs: ${{ runner.os }}-pods- - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 - name: Dependencies working-directory: ./dashsync/Example run: pod install --repo-update diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 205e35954..d8a98852b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,6 +52,11 @@ jobs: - name: Rustup add targets run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim + - name: Setup CocoaPods + uses: maxim-lobanov/setup-cocoapods@v1 + with: + version: 1.15.2 + - name: Dependencies working-directory: ./dashsync/Example run: pod install --repo-update From e1e27bd02455f35d8cb20ea8e2240ca506e48642 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Mon, 17 Mar 2025 20:40:22 +0700 Subject: [PATCH 94/95] chore: bump dash-shared-core version --- DashSync.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DashSync.podspec b/DashSync.podspec index 786084ecb..a3fc3e9eb 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.5.0' + s.dependency 'DashSharedCore', '0.5.1' s.dependency 'CocoaLumberjack', '3.7.2' s.ios.dependency 'DWAlertController', '0.2.1' s.dependency 'DSDynamicOptions', '0.1.2' From a2e753686fe0e436f48fddcf051b8a740ee1fdb0 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Wed, 26 Mar 2025 14:19:03 +0700 Subject: [PATCH 95/95] fix: DashSync build fix --- Example/DashSync/DSTransactionsViewController.m | 2 +- Example/Podfile.lock | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Example/DashSync/DSTransactionsViewController.m b/Example/DashSync/DSTransactionsViewController.m index 59ee24a93..040bf2b89 100644 --- a/Example/DashSync/DSTransactionsViewController.m +++ b/Example/DashSync/DSTransactionsViewController.m @@ -317,7 +317,7 @@ - (void)configureCell:(DSTransactionTableViewCell *)cell atIndexPath:(NSIndexPat DSAccount *account = [self.chainManager.chain firstAccountThatCanContainTransaction:tx]; uint64_t received = [tx.chain amountReceivedFromTransaction:tx], sent = [tx.chain amountSentByTransaction:tx], - balance = [account balanceAfterTransaction:tx]; + balance = [account balance]; uint32_t blockHeight = self.blockHeight; uint32_t confirms = (tx.blockHeight > blockHeight) ? 0 : (blockHeight - tx.blockHeight) + 1; diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 7c01d39f9..328f060fd 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -586,10 +586,11 @@ PODS: - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - DAPI-GRPC/Messages - gRPC-ProtoRPC - - DashSharedCore (0.4.19) + - DashSharedCore (0.5.1) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) + - DashSharedCore (= 0.5.1) - DSDynamicOptions (= 0.1.2) - DWAlertController (= 0.2.1) - TinyCborObjc (= 0.4.6) @@ -666,7 +667,6 @@ PODS: 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) @@ -679,6 +679,7 @@ SPEC REPOS: - BoringSSL-GRPC - CocoaLumberjack - DAPI-GRPC + - DashSharedCore - DSDynamicOptions - DWAlertController - gRPC @@ -695,8 +696,6 @@ EXTERNAL SOURCES: CocoaImageHashing: :commit: ad01eee :git: https://github.com/ameingast/cocoaimagehashing.git - DashSharedCore: - :path: "../../dash-shared-core/" DashSync: :path: "../" @@ -713,8 +712,8 @@ SPEC CHECKSUMS: CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: 009f29640756017406ee2b0ab5f1073f1856f85e - DashSync: 2438dbf626f13a8633ccc19c718c1c223c8ee831 + DashSharedCore: b8481feb5f08acf162b548edbfc7a9b1ce491141 + DashSync: 1f5741aad267dcc0cfc04b6903490d304232ddf2 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f @@ -727,6 +726,6 @@ SPEC CHECKSUMS: tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80 -PODFILE CHECKSUM: f435b90d50cf6c9c6e0622b30150618588fb4a56 +PODFILE CHECKSUM: 426e3f03a062ea4c149b0500478f0fe6bbe5bab5 COCOAPODS: 1.15.2