diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9a8376fe0..349893bce 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -45,7 +45,7 @@ jobs:
${{ runner.os }}-pods-
- uses: maxim-lobanov/setup-xcode@v1
with:
- xcode-version: '13.2.1'
+ xcode-version: '15.4'
- name: Rustup add targets
run: rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
- name: Dependencies
diff --git a/DashSync/iOS/Models/Managers/Service Managers/Auth/Controllers/ChildControllers/DSPassphraseChildViewController.m b/DashSync/iOS/Models/Managers/Service Managers/Auth/Controllers/ChildControllers/DSPassphraseChildViewController.m
index 61b2aebab..10c0aca01 100644
--- a/DashSync/iOS/Models/Managers/Service Managers/Auth/Controllers/ChildControllers/DSPassphraseChildViewController.m
+++ b/DashSync/iOS/Models/Managers/Service Managers/Auth/Controllers/ChildControllers/DSPassphraseChildViewController.m
@@ -98,7 +98,7 @@ - (void)verifySeedPharse {
oldData = getKeychainData(EXTENDED_0_PUBKEY_KEY_BIP44_V0, nil);
}
- NSData *seed = [bip39Mnemonic deriveKeyFromPhrase:[bip39Mnemonic normalizePhrase:phrase] withPassphrase:nil];
+ //NSData *seed = [bip39Mnemonic deriveKeyFromPhrase:[bip39Mnemonic normalizePhrase:phrase] withPassphrase:nil];
DSWallet *transientWallet = [DSWallet standardWalletWithSeedPhrase:phrase setCreationDate:[NSDate timeIntervalSince1970] forChain:chain storeSeedPhrase:NO isTransient:YES];
DSAccount *transientAccount = [transientWallet accountWithNumber:0];
DSDerivationPath *transientDerivationPath = [transientAccount bip44DerivationPath];
diff --git a/DashSync/shared/Categories/NSData/NSData+Dash.m b/DashSync/shared/Categories/NSData/NSData+Dash.m
index 8efea528a..ae9ddb2a4 100644
--- a/DashSync/shared/Categories/NSData/NSData+Dash.m
+++ b/DashSync/shared/Categories/NSData/NSData+Dash.m
@@ -1188,17 +1188,23 @@ - (uint8_t)UInt8AtOffset:(NSUInteger)offset {
- (uint16_t)UInt16AtOffset:(NSUInteger)offset {
if (self.length < offset + sizeof(uint16_t)) return 0;
- return CFSwapInt16LittleToHost(*(const uint16_t *)((const uint8_t *)self.bytes + offset));
+ uint16_t value;
+ memcpy(&value, (const uint8_t *)self.bytes + offset, sizeof(uint16_t));
+ return CFSwapInt16LittleToHost(value);
}
- (uint16_t)UInt16BigToHostAtOffset:(NSUInteger)offset {
if (self.length < offset + sizeof(uint16_t)) return 0;
- return CFSwapInt16BigToHost(*(const uint16_t *)((const uint8_t *)self.bytes + offset));
+ uint16_t value;
+ memcpy(&value, (const uint8_t *)self.bytes + offset, sizeof(uint16_t));
+ return CFSwapInt16BigToHost(value);
}
- (uint32_t)UInt32AtOffset:(NSUInteger)offset {
if (self.length < offset + sizeof(uint32_t)) return 0;
- return CFSwapInt32LittleToHost(*(const uint32_t *)((const uint8_t *)self.bytes + offset));
+ uint32_t value;
+ memcpy(&value, (const uint8_t *)self.bytes + offset, sizeof(uint32_t));
+ return CFSwapInt32LittleToHost(value);
}
- (uint32_t)UInt32BigToHostAtOffset:(NSUInteger)offset {
@@ -1208,12 +1214,16 @@ - (uint32_t)UInt32BigToHostAtOffset:(NSUInteger)offset {
- (uint64_t)UInt64AtOffset:(NSUInteger)offset {
if (self.length < offset + sizeof(uint64_t)) return 0;
- return CFSwapInt64LittleToHost(*(const uint64_t *)((const uint8_t *)self.bytes + offset));
+ uint64_t value;
+ memcpy(&value, (const uint8_t *)self.bytes + offset, sizeof(uint64_t));
+ return CFSwapInt64LittleToHost(value);
}
- (int64_t)Int64AtOffset:(NSUInteger)offset {
if (self.length < offset + sizeof(int64_t)) return 0;
- return CFSwapInt64LittleToHost(*(const int64_t *)((const uint8_t *)self.bytes + offset));
+ uint64_t value;
+ memcpy(&value, (const uint8_t *)self.bytes + offset, sizeof(uint64_t));
+ return CFSwapInt64LittleToHost(value);
}
- (UInt128)UInt128AtOffset:(NSUInteger)offset {
diff --git a/DashSync/shared/Categories/NSString+Bitcoin.m b/DashSync/shared/Categories/NSString+Bitcoin.m
index bf6578016..b48e2f8a2 100644
--- a/DashSync/shared/Categories/NSString+Bitcoin.m
+++ b/DashSync/shared/Categories/NSString+Bitcoin.m
@@ -354,13 +354,11 @@ - (NSData *)base58ToData {
- (NSData *)base58checkToData {
NSData *d = self.base58ToData;
-
if (d.length < 4) return nil;
-
NSData *data = CFBridgingRelease(CFDataCreate(SecureAllocator(), d.bytes, d.length - 4));
-
- // verify checksum
- if (*(uint32_t *)((const uint8_t *)d.bytes + d.length - 4) != data.SHA256_2.u32[0]) return nil;
+ uint32_t checksum;
+ memcpy(&checksum, (const uint8_t *)d.bytes + d.length - 4, sizeof(uint32_t));
+ if (checksum != data.SHA256_2.u32[0]) return nil;
return data;
}
diff --git a/DashSync/shared/DashSync.h b/DashSync/shared/DashSync.h
index 69f1fb352..7f28d2347 100644
--- a/DashSync/shared/DashSync.h
+++ b/DashSync/shared/DashSync.h
@@ -55,6 +55,7 @@
#import "DSPriceManager.h"
#import "DSShapeshiftManager.h"
#import "DSSporkManager.h"
+#import "DSSyncState.h"
#import "DSTransactionManager.h"
#import "DSVersionManager.h"
#import "DSWallet.h"
diff --git a/DashSync/shared/DashSync.m b/DashSync/shared/DashSync.m
index e97d7c9a0..25d8f807b 100644
--- a/DashSync/shared/DashSync.m
+++ b/DashSync/shared/DashSync.m
@@ -18,6 +18,7 @@
#import "DSMerkleBlockEntity+CoreDataClass.h"
#import "DSPeerEntity+CoreDataClass.h"
#import "DSPeerManager+Protected.h"
+#import "DSSyncState.h"
#import "DSQuorumEntryEntity+CoreDataClass.h"
#import "DSQuorumSnapshotEntity+CoreDataClass.h"
#import "DSSporkManager+Protected.h"
@@ -168,12 +169,10 @@ - (void)wipeBlockchainDataForChain:(DSChain *)chain inContext:(NSManagedObjectCo
[DSDashpayUserEntity deleteContactsOnChainEntity:chainEntity]; // this must move after wipeBlockchainInfo where blockchain identities are removed
[context ds_save];
[chain reloadDerivationPaths];
- [chain.chainManager assignSyncWeights];
+ [chain.chainManager notifySyncStateChanged];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DSWalletBalanceDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainChainSyncBlocksDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainTerminalBlocksDidChangeNotification object:nil];
});
}];
}
@@ -198,12 +197,10 @@ - (void)wipeBlockchainNonTerminalDataForChain:(DSChain *)chain inContext:(NSMana
[DSDashpayUserEntity deleteContactsOnChainEntity:chainEntity]; // this must move after wipeBlockchainInfo where blockchain identities are removed
[context ds_save];
[chain reloadDerivationPaths];
- [chain.chainManager assignSyncWeights];
+ [chain.chainManager notifySyncStateChanged];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DSWalletBalanceDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainChainSyncBlocksDidChangeNotification object:nil];
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainTerminalBlocksDidChangeNotification object:nil];
});
}];
}
@@ -222,7 +219,7 @@ - (void)wipeMasternodeDataForChain:(DSChain *)chain inContext:(NSManagedObjectCo
DSChainManager *chainManager = [[DSChainsManager sharedInstance] chainManagerForChain:chain];
[chainManager wipeMasternodeInfo];
[context ds_save];
- [chain.chainManager assignSyncWeights];
+ [chain.chainManager notifySyncStateChanged];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[NSString stringWithFormat:@"%@_%@", chain.uniqueID, LAST_SYNCED_MASTERNODE_LIST]];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:DSMasternodeListDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: chain}];
@@ -325,7 +322,7 @@ - (void)scheduleBackgroundFetch {
- (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
DSChainManager *mainnetManager = [[DSChainsManager sharedInstance] mainnetManager];
- if (mainnetManager.chainSyncProgress >= 1.0) {
+ if (mainnetManager.syncState.chainSyncProgress >= 1.0) {
DSLog(@"Background fetch: already synced");
if (completionHandler) {
@@ -374,7 +371,7 @@ - (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))com
}
- (void)backgroundFetchTimedOut {
- const double syncProgress = [[DSChainsManager sharedInstance] mainnetManager].chainSyncProgress;
+ const double syncProgress = [[DSChainsManager sharedInstance] mainnetManager].syncState.chainSyncProgress;
DSLog(@"Background fetch timeout with progress: %f", syncProgress);
const UIBackgroundFetchResult fetchResult = syncProgress > 0.1 ? UIBackgroundFetchResultNewData : UIBackgroundFetchResultFailed;
diff --git a/DashSync/shared/MainnetFixedPeers.plist b/DashSync/shared/MainnetFixedPeers.plist
new file mode 100644
index 000000000..6420dffcc
--- /dev/null
+++ b/DashSync/shared/MainnetFixedPeers.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ 3263651171
+ 148804577
+ 759996581
+ 2994784866
+ 2673411509
+ 2297634243
+ 1588652144
+ 2178245180
+ 2671888933
+ 94492658
+ 3156785217
+ 759997248
+ 3156779795
+ 3156782919
+ 3156743658
+ 1389566409
+ 2461642608
+ 2297663013
+ 1097723841
+ 1389566384
+ 2335935255
+ 822870522
+ 1608023587
+ 1311700836
+ 759130970
+ 2178245360
+
+
diff --git a/DashSync/shared/Models/Chain/DSChain.h b/DashSync/shared/Models/Chain/DSChain.h
index f28612750..4537474e8 100644
--- a/DashSync/shared/Models/Chain/DSChain.h
+++ b/DashSync/shared/Models/Chain/DSChain.h
@@ -34,15 +34,12 @@ NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSString *const DSChainWalletsDidChangeNotification;
FOUNDATION_EXPORT NSString *const DSChainStandaloneDerivationPathsDidChangeNotification;
FOUNDATION_EXPORT NSString *const DSChainStandaloneAddressesDidChangeNotification;
-FOUNDATION_EXPORT NSString *const DSChainChainSyncBlocksDidChangeNotification;
FOUNDATION_EXPORT NSString *const DSChainBlockWasLockedNotification;
FOUNDATION_EXPORT NSString *const DSChainNotificationBlockKey;
// For improved performance DSChainInitialHeadersDidChangeNotification is not garanteed to trigger on every initial headers change.
-FOUNDATION_EXPORT NSString *const DSChainTerminalBlocksDidChangeNotification;
FOUNDATION_EXPORT NSString *const DSChainInitialHeadersDidFinishSyncingNotification;
FOUNDATION_EXPORT NSString *const DSChainBlocksDidFinishSyncingNotification;
-FOUNDATION_EXPORT NSString *const DSChainNewChainTipBlockNotification;
typedef NS_ENUM(NSUInteger, DSTransactionDirection)
{
@@ -504,6 +501,7 @@ typedef NS_ENUM(uint16_t, DSChainSyncPhase)
@required
+- (void)chainWillStartConnectingToPeers:(DSChain *)chain;
- (void)chainWillStartSyncingBlockchain:(DSChain *)chain;
- (void)chainShouldStartSyncingBlockchain:(DSChain *)chain onPeer:(DSPeer *)peer;
- (void)chainFinishedSyncingTransactionsAndBlocks:(DSChain *)chain fromPeer:(DSPeer *_Nullable)peer onMainChain:(BOOL)onMainChain;
diff --git a/DashSync/shared/Models/Chain/DSChain.m b/DashSync/shared/Models/Chain/DSChain.m
index d9b0e0d2a..834465ffc 100644
--- a/DashSync/shared/Models/Chain/DSChain.m
+++ b/DashSync/shared/Models/Chain/DSChain.m
@@ -162,8 +162,6 @@ @interface DSChain ()
@property (nonatomic, strong) DSCheckpoint *terminalHeadersOverrideUseCheckpoint;
@property (nonatomic, strong) DSCheckpoint *syncHeadersOverrideUseCheckpoint;
@property (nonatomic, strong) DSCheckpoint *lastCheckpoint;
-@property (nonatomic, assign) NSTimeInterval lastNotifiedBlockDidChange;
-@property (nonatomic, strong) NSTimer *lastNotifiedBlockDidChangeTimer;
@property (nonatomic, assign, getter=isTransient) BOOL transient;
@property (nonatomic, assign) BOOL cachedIsQuorumRotationPresented;
@property (nonatomic, readonly) NSString *chainWalletsKey;
@@ -185,9 +183,7 @@ - (instancetype)init {
self.transactionHashHeights = [NSMutableDictionary dictionary];
self.transactionHashTimestamps = [NSMutableDictionary dictionary];
-
- self.lastNotifiedBlockDidChange = 0;
-
+
if (self.checkpoints) {
self.genesisHash = self.checkpoints[0].blockHash;
dispatch_sync(self.networkingQueue, ^{
@@ -1201,9 +1197,7 @@ - (void)unregisterStandaloneDerivationPath:(DSDerivationPath *)derivationPath {
[keyChainArray removeObject:derivationPath.standaloneExtendedPublicKeyUniqueID];
setKeychainArray(keyChainArray, self.chainStandaloneDerivationPathsKey, NO);
[self.viewingAccount removeDerivationPath:derivationPath];
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainStandaloneDerivationPathsDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
+ [self notify:DSChainStandaloneDerivationPathsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}];
}
- (void)addStandaloneDerivationPath:(DSDerivationPath *)derivationPath {
[self.viewingAccount addDerivationPath:derivationPath];
@@ -1218,9 +1212,7 @@ - (void)registerStandaloneDerivationPath:(DSDerivationPath *)derivationPath {
if (!keyChainArray) keyChainArray = [NSMutableArray array];
[keyChainArray addObject:derivationPath.standaloneExtendedPublicKeyUniqueID];
setKeychainArray(keyChainArray, self.chainStandaloneDerivationPathsKey, NO);
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainStandaloneDerivationPathsDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
+ [self notify:DSChainStandaloneDerivationPathsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}];
}
- (NSArray *)standaloneDerivationPaths {
@@ -1414,9 +1406,7 @@ - (void)unregisterWallet:(DSWallet *)wallet {
if (!keyChainArray) keyChainArray = [NSMutableArray array];
[keyChainArray removeObject:wallet.uniqueIDString];
setKeychainArray(keyChainArray, self.chainWalletsKey, NO);
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainWalletsDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
+ [self notify:DSChainWalletsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}];
}
- (BOOL)addWallet:(DSWallet *)walletToAdd {
@@ -1450,9 +1440,7 @@ - (void)registerWallet:(DSWallet *)wallet {
if (![keyChainArray containsObject:wallet.uniqueIDString]) {
[keyChainArray addObject:wallet.uniqueIDString];
setKeychainArray(keyChainArray, self.chainWalletsKey, NO);
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainWalletsDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
+ [self notify:DSChainWalletsDidChangeNotification userInfo:@{DSChainManagerNotificationChainKey: self}];
}
}
@@ -1544,7 +1532,7 @@ - (void)setLastSyncBlockFromCheckpoints {
}
if (_lastSyncBlock) {
- DSLog(@"[%@] last sync block at height %d chosen from checkpoints (hash is %@)", self.name, _lastSyncBlock.height, self.name, [NSData dataWithUInt256:_lastSyncBlock.blockHash].hexString);
+ DSLog(@"[%@] last sync block at height %d chosen from checkpoints (hash is %@)", self.name, _lastSyncBlock.height, [NSData dataWithUInt256:_lastSyncBlock.blockHash].hexString);
}
}
@@ -1779,9 +1767,9 @@ - (BOOL)addMinedFullBlock:(DSFullBlock *)block {
}
[self saveBlockLocators];
[self saveTerminalBlocks];
-
+ self.chainManager.syncState.estimatedBlockHeight = _bestEstimatedBlockHeight;
// notify that transaction confirmations may have changed
- [self notifyBlocksChanged];
+ [self.chainManager notifySyncStateChanged];
}
return TRUE;
@@ -1949,13 +1937,14 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
[block setChainLockedWithEquivalentBlock:equivalentTerminalBlock];
}
self.lastSyncBlock = block;
-
+ self.chainManager.syncState.lastSyncBlockHeight = block.height;
if (!equivalentTerminalBlock && uint256_eq(block.prevBlock, self.lastTerminalBlock.blockHash)) {
if ((h % 1000) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) {
DSLog(@"%@ + terminal block (caught up) at: %d: %@", prefix, h, uint256_hex(block.blockHash));
}
self.mTerminalBlocks[blockHash] = block;
self.lastTerminalBlock = block;
+ self.chainManager.syncState.lastTerminalBlockHeight = block.height;
}
@synchronized(peer) {
if (peer) {
@@ -1977,6 +1966,9 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
}
self.mTerminalBlocks[blockHash] = block;
self.lastTerminalBlock = block;
+ self.chainManager.syncState.estimatedBlockHeight = self.estimatedBlockHeight;
+ self.chainManager.syncState.lastTerminalBlockHeight = block.height;
+
@synchronized(peer) {
if (peer) {
peer.currentBlockHeight = h; //might be download peer instead
@@ -2005,7 +1997,11 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
if (b != nil && uint256_eq(b.blockHash, block.blockHash)) { // if it's not on a fork, set block heights for its transactions
[self setBlockHeight:h andTimestamp:txTime forTransactionHashes:txHashes];
- if (h == self.lastSyncBlockHeight) self.lastSyncBlock = block;
+ if (h == self.lastSyncBlockHeight) {
+ self.lastSyncBlock = block;
+ self.chainManager.syncState.estimatedBlockHeight = self.estimatedBlockHeight;
+ self.chainManager.syncState.lastSyncBlockHeight = block.height;
+ }
}
} else if (self.mTerminalBlocks[blockHash] != nil && (blockPosition & DSBlockPosition_Terminal)) { // we already have the block (or at least the header)
if ((h % 1) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) {
@@ -2024,7 +2020,10 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
if (b != nil && uint256_eq(b.blockHash, block.blockHash)) { // if it's not on a fork, set block heights for its transactions
[self setBlockHeight:h andTimestamp:txTime forTransactionHashes:txHashes];
- if (h == self.lastTerminalBlockHeight) self.lastTerminalBlock = block;
+ if (h == self.lastTerminalBlockHeight) {
+ self.lastTerminalBlock = block;
+ self.chainManager.syncState.lastTerminalBlockHeight = block.height;
+ }
}
} else { // new block is on a fork
if (h <= [self lastCheckpoint].height) { // fork is older than last checkpoint
@@ -2059,6 +2058,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
DSLog(@"%@ reorganizing terminal chain from height %d, new height is %d", prefix, b.height, h);
self.lastTerminalBlock = block;
+ self.chainManager.syncState.lastTerminalBlockHeight = block.height;
@synchronized(peer) {
if (peer) {
peer.currentBlockHeight = h; //might be download peer instead
@@ -2090,6 +2090,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
} else {
DSLog(@"%@ reorganizing terminal chain from height %d, new height is %d", prefix, b.height, h);
self.lastTerminalBlock = block;
+ self.chainManager.syncState.lastTerminalBlockHeight = block.height;
@synchronized(peer) {
if (peer) {
peer.currentBlockHeight = h; //might be download peer instead
@@ -2131,6 +2132,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
}
self.lastSyncBlock = block;
+ self.chainManager.syncState.lastSyncBlockHeight = block.height;
if (h == self.estimatedBlockHeight) syncDone = YES;
}
}
@@ -2148,9 +2150,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
if (peer) {
[self.chainManager chainFinishedSyncingInitialHeaders:self fromPeer:peer onMainChain:onMainChain];
}
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainInitialHeadersDidFinishSyncingNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
+ [self notify:DSChainInitialHeadersDidFinishSyncingNotification userInfo:@{DSChainManagerNotificationChainKey: self}];
}
if ((blockPosition & DSBlockPosition_Sync) && (phase == DSChainSyncPhase_ChainSync || phase == DSChainSyncPhase_Synced)) {
//we should only save
@@ -2159,15 +2159,14 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
if (peer) {
[self.chainManager chainFinishedSyncingTransactionsAndBlocks:self fromPeer:peer onMainChain:onMainChain];
}
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainBlocksDidFinishSyncingNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
+ [self notify:DSChainBlocksDidFinishSyncingNotification userInfo:@{DSChainManagerNotificationChainKey: self}];
}
}
if (((blockPosition & DSBlockPosition_Terminal) && block.height > self.estimatedBlockHeight) || ((blockPosition & DSBlockPosition_Sync) && block.height >= self.lastTerminalBlockHeight)) {
@synchronized (self) {
_bestEstimatedBlockHeight = block.height;
+ self.chainManager.syncState.estimatedBlockHeight = _bestEstimatedBlockHeight;
}
if (peer && (blockPosition & DSBlockPosition_Sync) && !savedBlockLocators) {
[self saveBlockLocators];
@@ -2178,18 +2177,8 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
if (peer) {
[self.chainManager chain:self wasExtendedWithBlock:block fromPeer:peer];
}
-
- // notify that transaction confirmations may have changed
- [self setupBlockChangeTimer:^{
- [self notifyBlocksChanged];
- }];
- } else {
- //we should avoid dispatching this message too frequently
- [self setupBlockChangeTimer:^{
- [self notifyBlocksChanged:blockPosition];
- }];
}
-
+ [self.chainManager notifySyncStateChanged];
// check if the next block was received as an orphan
if (block == self.lastTerminalBlock && self.mOrphans[blockHash]) {
DSBlock *b = self.mOrphans[blockHash];
@@ -2200,45 +2189,15 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:(
return TRUE;
}
-- (void)setupBlockChangeTimer:(void (^ __nullable)(void))completion {
- //we should avoid dispatching this message too frequently
- NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
- if (!self.lastNotifiedBlockDidChange || (timestamp - self.lastNotifiedBlockDidChange > 0.1)) {
- self.lastNotifiedBlockDidChange = timestamp;
- if (self.lastNotifiedBlockDidChangeTimer) {
- [self.lastNotifiedBlockDidChangeTimer invalidate];
- self.lastNotifiedBlockDidChangeTimer = nil;
- }
- completion();
- } else if (!self.lastNotifiedBlockDidChangeTimer) {
- self.lastNotifiedBlockDidChangeTimer = [NSTimer timerWithTimeInterval:1 repeats:NO block:^(NSTimer *_Nonnull timer) {
- completion();
- }];
- [[NSRunLoop mainRunLoop] addTimer:self.lastNotifiedBlockDidChangeTimer forMode:NSRunLoopCommonModes];
- }
-}
-
-- (void)notifyBlocksChanged {
+- (void)notify:(NSNotificationName)name userInfo:(NSDictionary *_Nullable)userInfo {
dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainNewChainTipBlockNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainChainSyncBlocksDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainTerminalBlocksDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
+ [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:userInfo];
});
}
-- (void)notifyBlocksChanged:(DSBlockPosition)blockPosition {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (blockPosition & DSBlockPosition_Terminal) {
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainTerminalBlocksDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- }
- if (blockPosition & DSBlockPosition_Sync) {
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainChainSyncBlocksDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- }
-
- });
-}
// MARK: Terminal Blocks
+
- (NSMutableDictionary *)mTerminalBlocks {
@synchronized (_mTerminalBlocks) {
if (_mTerminalBlocks.count > 0) {
@@ -2371,6 +2330,8 @@ - (BOOL)addChainLock:(DSChainLock *)chainLock {
DSLog(@"[%@] Reorginizing to height %d", self.name, clb.height);
self.lastTerminalBlock = terminalBlock;
+ self.chainManager.syncState.lastTerminalBlockHeight = terminalBlock.height;
+ [self.chainManager notifySyncStateChanged];
NSMutableDictionary *forkChainsTerminalBlocks = [[self forkChainsTerminalBlocks] mutableCopy];
NSMutableArray *addedBlocks = [NSMutableArray array];
BOOL done = FALSE;
@@ -2422,7 +2383,8 @@ - (BOOL)addChainLock:(DSChainLock *)chainLock {
DSLog(@"[%@] Cancelling sync reorg because block %@ is already chain locked", self.name, sbmc);
} else {
self.lastSyncBlock = syncBlock;
-
+ self.chainManager.syncState.lastSyncBlockHeight = syncBlock.height;
+ [self.chainManager notifySyncStateChanged];
DSLog(@"[%@] Reorginizing to height %d (last sync block %@)", self.name, clb.height, self.lastSyncBlock);
@@ -2724,17 +2686,10 @@ - (void)setEstimatedBlockHeight:(uint32_t)estimatedBlockHeight fromPeer:(DSPeer
uint32_t finalEstimatedBlockHeight = [self decideFromPeerSoftConsensusEstimatedBlockHeight];
if (finalEstimatedBlockHeight > oldEstimatedBlockHeight) {
_bestEstimatedBlockHeight = finalEstimatedBlockHeight;
- dispatch_once(&onceToken, ^{
- [self.chainManager assignSyncWeights];
- });
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncParametersUpdatedNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self}];
- });
- } else {
- dispatch_once(&onceToken, ^{
- [self.chainManager assignSyncWeights];
- });
}
+ dispatch_once(&onceToken, ^{
+ self.chainManager.syncState.estimatedBlockHeight = finalEstimatedBlockHeight;
+ });
}
}
@@ -2750,6 +2705,7 @@ - (void)removeEstimatedBlockHeightOfPeer:(DSPeer *)peer {
//keep best estimate if no other peers reporting on estimate
if ([self.estimatedBlockHeights count] && ([height intValue] == _bestEstimatedBlockHeight)) {
_bestEstimatedBlockHeight = 0;
+ self.chainManager.syncState.estimatedBlockHeight = 0;
}
}
}
diff --git a/DashSync/shared/Models/Chain/DSChainConstants.h b/DashSync/shared/Models/Chain/DSChainConstants.h
index 5bc90d373..4420efbfc 100644
--- a/DashSync/shared/Models/Chain/DSChainConstants.h
+++ b/DashSync/shared/Models/Chain/DSChainConstants.h
@@ -31,13 +31,13 @@
#define TESTNET_DAPI_GRPC_STANDARD_PORT 3010
#define DEVNET_DAPI_GRPC_STANDARD_PORT 3010
-#define PROTOCOL_VERSION_MAINNET 70231
+#define PROTOCOL_VERSION_MAINNET 70232
#define DEFAULT_MIN_PROTOCOL_VERSION_MAINNET 70228
-#define PROTOCOL_VERSION_TESTNET 70231
+#define PROTOCOL_VERSION_TESTNET 70232
#define DEFAULT_MIN_PROTOCOL_VERSION_TESTNET 70228
-#define PROTOCOL_VERSION_DEVNET 70231
+#define PROTOCOL_VERSION_DEVNET 70232
#define DEFAULT_MIN_PROTOCOL_VERSION_DEVNET 70228
#define PLATFORM_PROTOCOL_VERSION_MAINNET 1
diff --git a/DashSync/shared/Models/Entities/DSCoinbaseTransactionEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSCoinbaseTransactionEntity+CoreDataClass.m
index ef06f6804..18cbc6745 100644
--- a/DashSync/shared/Models/Entities/DSCoinbaseTransactionEntity+CoreDataClass.m
+++ b/DashSync/shared/Models/Entities/DSCoinbaseTransactionEntity+CoreDataClass.m
@@ -23,7 +23,7 @@ - (instancetype)setAttributesFromTransaction:(DSTransaction *)tx {
if (self.specialTransactionVersion >= COINBASE_TX_CORE_19) {
self.merkleRootLLMQList = uint256_data(coinbaseTransaction.merkleRootLLMQList);
if (self.specialTransactionVersion >= COINBASE_TX_CORE_20) {
- self.bestCLHeightDiff = coinbaseTransaction.bestCLHeightDiff;
+ self.bestCLHeightDiff = (uint32_t) coinbaseTransaction.bestCLHeightDiff;
self.bestCLSignature = uint768_data(coinbaseTransaction.bestCLSignature);
self.creditPoolBalance = coinbaseTransaction.creditPoolBalance;
}
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSBackgroundManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSBackgroundManager.h
new file mode 100644
index 000000000..e947359ef
--- /dev/null
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSBackgroundManager.h
@@ -0,0 +1,35 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+#import "DSChain.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DSBackgroundManager : NSObject
+
+- (instancetype)initWithChain:(DSChain *)chain;
+
+- (void)createBlockLocatorsTask:(void(^ __nullable)(void))handler;
+- (void)createTerminalHeadersTask:(void(^ __nullable)(void))handler;
+- (void)stopBackgroundActivities;
+
+- (BOOL)hasValidHeadersTask;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSBackgroundManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSBackgroundManager.m
new file mode 100644
index 000000000..e579e6a1f
--- /dev/null
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSBackgroundManager.m
@@ -0,0 +1,92 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "DSBackgroundManager.h"
+#import "DSChainManager.h"
+#import "DSPeerManager+Protected.h"
+
+@interface DSBackgroundManager ()
+
+@property (nonatomic, strong) DSChain *chain;
+
+#if TARGET_OS_IOS
+
+@property (nonatomic, strong) id backgroundObserver;
+@property (nonatomic, assign) NSUInteger terminalHeadersSaveTaskId, blockLocatorsSaveTaskId;
+
+#endif
+
+@end
+
+@implementation DSBackgroundManager
+
+- (instancetype)initWithChain:(DSChain *)chain {
+ if (!(self = [super init])) return nil;
+#if TARGET_OS_IOS
+ self.terminalHeadersSaveTaskId = UIBackgroundTaskInvalid;
+ self.backgroundObserver =
+ [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
+ [chain.chainManager.peerManager startBackgroundMode:self.terminalHeadersSaveTaskId == UIBackgroundTaskInvalid];
+ }];
+#endif
+
+ return self;
+}
+
+- (void)dealloc {
+#if TARGET_OS_IOS
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ if (self.backgroundObserver)
+ [[NSNotificationCenter defaultCenter] removeObserver:self.backgroundObserver];
+#endif
+}
+
+
+- (void)createBlockLocatorsTask:(void(^ __nullable)(void))handler {
+#if TARGET_OS_IOS
+ if (self.blockLocatorsSaveTaskId == UIBackgroundTaskInvalid) { // start a background task for the chain sync
+ self.blockLocatorsSaveTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:handler];
+ }
+#endif
+}
+- (void)createTerminalHeadersTask:(void(^ __nullable)(void))handler {
+#if TARGET_OS_IOS
+ if (self.terminalHeadersSaveTaskId == UIBackgroundTaskInvalid) { // start a background task for the chain sync
+ self.terminalHeadersSaveTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:handler];
+ }
+#endif
+}
+
+- (void)stopBackgroundActivities {
+#if TARGET_OS_IOS
+ if (self.terminalHeadersSaveTaskId != UIBackgroundTaskInvalid) {
+ [[UIApplication sharedApplication] endBackgroundTask:self.terminalHeadersSaveTaskId];
+ self.terminalHeadersSaveTaskId = UIBackgroundTaskInvalid;
+ }
+
+ if (self.blockLocatorsSaveTaskId != UIBackgroundTaskInvalid) {
+ [[UIApplication sharedApplication] endBackgroundTask:self.blockLocatorsSaveTaskId];
+ self.blockLocatorsSaveTaskId = UIBackgroundTaskInvalid;
+ }
+#endif
+}
+
+- (BOOL)hasValidHeadersTask {
+ return self.terminalHeadersSaveTaskId != UIBackgroundTaskInvalid || [UIApplication sharedApplication].applicationState != UIApplicationStateBackground;
+}
+
+@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Mining.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Mining.h
new file mode 100644
index 000000000..e1bdddfdb
--- /dev/null
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Mining.h
@@ -0,0 +1,54 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+#import "DSChainManager.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DSChainManager (Mining)
+
+// MARK: - Mining
+
+- (void)mineEmptyBlocks:(uint32_t)blockCount
+ toPaymentAddress:(NSString *)paymentAddress
+ withTimeout:(NSTimeInterval)timeout
+ completion:(MultipleBlockMiningCompletionBlock)completion;
+
+- (void)mineEmptyBlocks:(uint32_t)blockCount
+ toPaymentAddress:(NSString *)paymentAddress
+ afterBlock:(DSBlock *)block
+ previousBlocks:(NSDictionary *)previousBlocks
+ withTimeout:(NSTimeInterval)timeout
+ completion:(MultipleBlockMiningCompletionBlock)completion;
+
+- (void)mineBlockToPaymentAddress:(NSString *)paymentAddress
+ withTransactions:(NSArray *_Nullable)transactions
+ withTimeout:(NSTimeInterval)timeout
+ completion:(BlockMiningCompletionBlock)completion;
+
+- (void)mineBlockAfterBlock:(DSBlock *)block
+ toPaymentAddress:(NSString *)paymentAddress
+ withTransactions:(NSArray *_Nullable)transactions
+ previousBlocks:(NSDictionary *)previousBlocks
+ nonceOffset:(uint32_t)nonceOffset
+ withTimeout:(NSTimeInterval)timeout
+ completion:(BlockMiningCompletionBlock)completion;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Mining.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Mining.m
new file mode 100644
index 000000000..8a92dea9c
--- /dev/null
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Mining.m
@@ -0,0 +1,104 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "DSChain+Protected.h"
+#import "DSChainManager+Protected.h"
+#import "DSChainManager+Mining.h"
+#import "DSFullBlock.h"
+#import "NSError+Dash.h"
+
+@implementation DSChainManager (Mining)
+
+- (void)mineEmptyBlocks:(uint32_t)blockCount
+ toPaymentAddress:(NSString *)paymentAddress
+ withTimeout:(NSTimeInterval)timeout
+ completion:(MultipleBlockMiningCompletionBlock)completion {
+ [self mineEmptyBlocks:blockCount toPaymentAddress:paymentAddress afterBlock:self.chain.lastTerminalBlock previousBlocks:self.chain.terminalBlocks withTimeout:timeout completion:completion];
+}
+
+- (void)mineEmptyBlocks:(uint32_t)blockCount
+ toPaymentAddress:(NSString *)paymentAddress
+ afterBlock:(DSBlock *)previousBlock
+ previousBlocks:(NSDictionary *)previousBlocks
+ withTimeout:(NSTimeInterval)timeout
+ completion:(MultipleBlockMiningCompletionBlock)completion {
+ dispatch_async(self.miningQueue, ^{
+ NSTimeInterval start = [[NSDate date] timeIntervalSince1970];
+ NSTimeInterval end = [[[NSDate alloc] initWithTimeIntervalSinceNow:timeout] timeIntervalSince1970];
+ NSMutableArray *blocksArray = [NSMutableArray array];
+ NSMutableArray *attemptsArray = [NSMutableArray array];
+ __block uint32_t blocksRemaining = blockCount;
+ __block NSMutableDictionary *mPreviousBlocks = [previousBlocks mutableCopy];
+ __block DSBlock *currentBlock = previousBlock;
+ while ([[NSDate date] timeIntervalSince1970] < end && blocksRemaining > 0) {
+ dispatch_semaphore_t sem = dispatch_semaphore_create(0);
+ [self mineBlockAfterBlock:currentBlock
+ toPaymentAddress:paymentAddress
+ withTransactions:[NSArray array]
+ previousBlocks:mPreviousBlocks
+ nonceOffset:0
+ withTimeout:timeout
+ completion:^(DSFullBlock *_Nullable block, NSUInteger attempts, NSTimeInterval timeUsed, NSError *_Nullable error) {
+ NSAssert(uint256_is_not_zero(block.blockHash), @"Block hash must not be empty");
+ dispatch_semaphore_signal(sem);
+ [blocksArray addObject:block];
+ [mPreviousBlocks setObject:block forKey:uint256_obj(block.blockHash)];
+ currentBlock = block;
+ blocksRemaining--;
+ [attemptsArray addObject:@(attempts)];
+ }];
+ dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+ }
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(blocksArray, attemptsArray, [[NSDate date] timeIntervalSince1970] - start, nil);
+ });
+ }
+ });
+}
+
+- (void)mineBlockToPaymentAddress:(NSString *)paymentAddress
+ withTransactions:(NSArray *)transactions
+ withTimeout:(NSTimeInterval)timeout
+ completion:(BlockMiningCompletionBlock)completion {
+ [self mineBlockAfterBlock:self.chain.lastTerminalBlock toPaymentAddress:paymentAddress withTransactions:transactions previousBlocks:self.chain.terminalBlocks nonceOffset:0 withTimeout:timeout completion:completion];
+}
+
+- (void)mineBlockAfterBlock:(DSBlock *)block
+ toPaymentAddress:(NSString *)paymentAddress
+ withTransactions:(NSArray *)transactions
+ previousBlocks:(NSDictionary *)previousBlocks
+ nonceOffset:(uint32_t)nonceOffset
+ withTimeout:(NSTimeInterval)timeout
+ completion:(nonnull BlockMiningCompletionBlock)completion {
+ DSCoinbaseTransaction *coinbaseTransaction = [[DSCoinbaseTransaction alloc] initWithCoinbaseMessage:@"From iOS" paymentAddresses:@[paymentAddress] atHeight:block.height + 1 onChain:block.chain];
+ DSFullBlock *fullblock = [[DSFullBlock alloc] initWithCoinbaseTransaction:coinbaseTransaction transactions:[NSSet set] previousBlockHash:block.blockHash previousBlocks:previousBlocks timestamp:[[NSDate date] timeIntervalSince1970] height:block.height + 1 onChain:self.chain];
+ uint64_t attempts = 0;
+ NSDate *startTime = [NSDate date];
+ if ([fullblock mineBlockAfterBlock:block withNonceOffset:nonceOffset withTimeout:timeout rAttempts:&attempts]) {
+ if (completion) {
+ completion(fullblock, attempts, -[startTime timeIntervalSinceNow], nil);
+ }
+ } else {
+ if (completion) {
+ NSError *error = [NSError errorWithCode:500 localizedDescriptionKey:@"A block could not be mined in the selected time interval."];
+ completion(nil, attempts, -[startTime timeIntervalSinceNow], error);
+ }
+ }
+}
+
+@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h
index 722e49ed1..e24ddb3fc 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Protected.h
@@ -5,32 +5,37 @@
// Created by Sam Westrich on 11/21/18.
//
+#import "DSChain.h"
#import "DSChainManager.h"
NS_ASSUME_NONNULL_BEGIN
+typedef NS_ENUM(uint16_t, DSChainNotificationType) {
+ DSChainNotificationType_Headers = 0,
+ DSChainNotificationType_Blocks = 1,
+ DSChainNotificationType_SyncState = 2,
+};
+
@interface DSChainManager ()
@property (nonatomic, assign) NSTimeInterval lastChainRelayTime;
+@property (nonatomic, assign) DSChainSyncPhase syncPhase;
+@property (nonatomic, strong) dispatch_queue_t miningQueue;
-- (instancetype)initWithChain:(DSChain *)chain;
-- (void)resetSyncCountInfo:(DSSyncCountInfo)masternodeSyncCountInfo inContext:(NSManagedObjectContext *)context;
- (void)resetChainSyncStartHeight;
- (void)restartChainSyncStartHeight;
- (void)resetTerminalSyncStartHeight;
- (void)restartTerminalSyncStartHeight;
+- (instancetype)initWithChain:(DSChain *)chain;
+- (void)resetSyncCountInfo:(DSSyncCountInfo)masternodeSyncCountInfo inContext:(NSManagedObjectContext *)context;
- (void)relayedNewItem;
-- (void)resetLastRelayedItemTime;
- (void)setCount:(uint32_t)count forSyncCountInfo:(DSSyncCountInfo)masternodeSyncCountInfo inContext:(NSManagedObjectContext *)context;
-- (BOOL)shouldRequestMerkleBlocksForZoneBetweenHeight:(uint32_t)blockHeight andEndHeight:(uint32_t)endBlockHeight;
-- (BOOL)shouldRequestMerkleBlocksForZoneAfterHeight:(uint32_t)blockHeight;
-
- (void)wipeMasternodeInfo;
-@property (nonatomic, assign) DSChainSyncPhase syncPhase;
+- (void)notify:(NSNotificationName)name userInfo:(NSDictionary *_Nullable)userInfo;
+- (void)notifySyncStateChanged;
-- (void)assignSyncWeights;
@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.h
new file mode 100644
index 000000000..7e17e178f
--- /dev/null
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.h
@@ -0,0 +1,30 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+#import "DSChainManager.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface DSChainManager (Transactions)
+
+- (BOOL)shouldRequestMerkleBlocksForZoneBetweenHeight:(uint32_t)blockHeight andEndHeight:(uint32_t)endBlockHeight;
+- (BOOL)shouldRequestMerkleBlocksForZoneAfterHeight:(uint32_t)blockHeight;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m
new file mode 100644
index 000000000..0ef4efefd
--- /dev/null
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager+Transactions.m
@@ -0,0 +1,208 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "DSChainManager+Protected.h"
+#import "DSChainManager+Transactions.h"
+#import "DSWallet+Protected.h"
+#import "RHIntervalTree.h"
+#import
+
+NSString const *maxTransactionsInfoDataKey = @"maxTransactionsInfoDataKey";
+NSString const *heightTransactionZonesKey = @"heightTransactionZonesKey";
+NSString const *maxTransactionsInfoDataFirstHeightKey = @"maxTransactionsInfoDataFirstHeightKey";
+NSString const *maxTransactionsInfoDataLastHeightKey = @"maxTransactionsInfoDataLastHeightKey";
+NSString const *chainSynchronizationFingerprintKey = @"chainSynchronizationFingerprintKey";
+NSString const *chainSynchronizationBlockZonesKey = @"chainSynchronizationBlockZonesKey";
+
+
+@interface DSChainManager ()
+
+@property (nonatomic, strong) NSData *maxTransactionsInfoData;
+@property (nonatomic, strong) RHIntervalTree *heightTransactionZones;
+@property (nonatomic, assign) uint32_t maxTransactionsInfoDataFirstHeight;
+@property (nonatomic, assign) uint32_t maxTransactionsInfoDataLastHeight;
+@property (nonatomic, strong) NSData *chainSynchronizationFingerprint;
+@property (nonatomic, strong) NSOrderedSet *chainSynchronizationBlockZones;
+
+@end
+
+@implementation DSChainManager (Transactions)
+
+- (void)setMaxTransactionsInfoData:(NSData *)maxTransactionsInfoData {
+ objc_setAssociatedObject(self, &maxTransactionsInfoDataKey, maxTransactionsInfoData, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+- (NSData *)maxTransactionsInfoData {
+ return objc_getAssociatedObject(self, &maxTransactionsInfoDataKey);
+}
+
+- (void)setHeightTransactionZones:(RHIntervalTree *)heightTransactionZones {
+ objc_setAssociatedObject(self, &heightTransactionZonesKey, heightTransactionZones, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+- (RHIntervalTree *)heightTransactionZones {
+ return objc_getAssociatedObject(self, &heightTransactionZonesKey);
+}
+
+
+- (void)setChainSynchronizationBlockZones:(NSOrderedSet *)chainSynchronizationBlockZones {
+ objc_setAssociatedObject(self, &chainSynchronizationBlockZonesKey, chainSynchronizationBlockZones, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+- (NSOrderedSet *)chainSynchronizationBlockZones {
+ NSOrderedSet *obj = objc_getAssociatedObject(self, &chainSynchronizationBlockZonesKey);
+ if (!obj) {
+ obj = [DSWallet blockZonesFromChainSynchronizationFingerprint:self.chainSynchronizationFingerprint rVersion:0 rChainHeight:0];
+ [self setChainSynchronizationBlockZones:obj];
+ }
+ return obj;
+
+}
+
+- (void)loadHeightTransactionZones {
+ NSString *bundlePath = [[NSBundle bundleForClass:self.class] pathForResource:@"DashSync" ofType:@"bundle"];
+ NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
+ NSString *filePath = [bundle pathForResource:[NSString stringWithFormat:@"HeightTransactionZones_%@", self.chain.name] ofType:@"dat"];
+ NSData *heightTransactionZonesData = [NSData dataWithContentsOfFile:filePath];
+ if (heightTransactionZonesData) {
+ NSMutableArray *intervals = [NSMutableArray array];
+ for (uint16_t i = 0; i < heightTransactionZonesData.length - 4; i += 4) {
+ uint32_t intervalStartHeight = [heightTransactionZonesData UInt16AtOffset:i] * 500;
+ uint16_t average = [heightTransactionZonesData UInt16AtOffset:i + 2];
+ uint32_t intervalEndHeight = [heightTransactionZonesData UInt16AtOffset:i + 4] * 500;
+ [intervals addObject:[RHInterval intervalWithStart:intervalStartHeight stop:intervalEndHeight - 1 object:@(average)]];
+ }
+ self.heightTransactionZones = [[RHIntervalTree alloc] initWithIntervalObjects:intervals];
+ }
+}
+
+- (uint16_t)averageTransactionsInZoneForStartHeight:(uint32_t)startHeight endHeight:(uint32_t)endHeight {
+ NSArray *intervals = [self.heightTransactionZones overlappingObjectsForStart:startHeight andStop:endHeight];
+ if (!intervals.count) return 0;
+ if (intervals.count == 1) return [(NSNumber *)[intervals[0] object] unsignedShortValue];
+ uint64_t aggregate = 0;
+ for (RHInterval *interval in intervals) {
+ uint64_t value = [(NSNumber *)interval.object unsignedLongValue];
+ if (interval == [intervals firstObject]) {
+ aggregate += value * (interval.stop - startHeight + 1);
+ } else if (interval == [intervals lastObject]) {
+ aggregate += value * (endHeight - interval.start + 1);
+ } else {
+ aggregate += value * (interval.stop - interval.start + 1);
+ }
+ }
+ return aggregate / (endHeight - startHeight);
+}
+
+- (uint32_t)firstHeightOutOfAverageRangeWithStart500RangeHeight:(uint32_t)height rAverage:(float *)rAverage {
+ return [self firstHeightOutOfAverageRangeWithStart500RangeHeight:height startingVarianceLevel:1 endingVarianceLevel:0.2 convergencePolynomial:0.33 rAverage:rAverage];
+}
+
+- (uint32_t)firstHeightOutOfAverageRangeWithStart500RangeHeight:(uint32_t)height startingVarianceLevel:(float)startingVarianceLevel endingVarianceLevel:(float)endingVarianceLevel convergencePolynomial:(float)convergencePolynomial rAverage:(float *)rAverage {
+ return [self firstHeightOutOfAverageRangeWithStart500RangeHeight:height startingVarianceLevel:startingVarianceLevel endingVarianceLevel:endingVarianceLevel convergencePolynomial:convergencePolynomial recursionLevel:0 recursionMaxLevel:2 rAverage:rAverage rAverages:nil];
+}
+
+- (uint32_t)firstHeightOutOfAverageRangeWithStart500RangeHeight:(uint32_t)height startingVarianceLevel:(float)startingVarianceLevel endingVarianceLevel:(float)endingVarianceLevel convergencePolynomial:(float)convergencePolynomial recursionLevel:(uint16_t)recursionLevel recursionMaxLevel:(uint16_t)recursionMaxLevel rAverage:(float *)rAverage rAverages:(NSArray **)rAverages {
+ NSMutableArray *averagesAtHeights = [NSMutableArray array];
+ float currentAverage = 0;
+ uint32_t checkHeight = height;
+ uint16_t i = 0;
+ float internalVarianceParameter = ((startingVarianceLevel - endingVarianceLevel) / endingVarianceLevel);
+ while (checkHeight < self.maxTransactionsInfoDataLastHeight) {
+ uint16_t averageValue = [self averageTransactionsFor500RangeAtHeight:checkHeight];
+
+ if (i != 0 && averageValue > 10) { //before 12 just ignore
+ float maxVariance = endingVarianceLevel * (powf((float)i, convergencePolynomial) + internalVarianceParameter) / powf((float)i, convergencePolynomial);
+ //NSLog(@"height %d averageValue %hu currentAverage %.2f variance %.2f",checkHeight,averageValue,currentAverage,fabsf(averageValue - currentAverage)/currentAverage);
+ if (fabsf(averageValue - currentAverage) > maxVariance * currentAverage) {
+ //there was a big change in variance
+ if (recursionLevel > recursionMaxLevel) break; //don't recurse again
+ //We need to make sure that this wasn't a 1 time variance
+ float nextAverage = 0;
+ NSArray *nextAverages = nil;
+
+ uint32_t nextHeight = [self firstHeightOutOfAverageRangeWithStart500RangeHeight:checkHeight startingVarianceLevel:startingVarianceLevel endingVarianceLevel:endingVarianceLevel convergencePolynomial:convergencePolynomial recursionLevel:recursionLevel + 1 recursionMaxLevel:recursionMaxLevel rAverage:&nextAverage rAverages:&nextAverages];
+ if (fabsf(nextAverage - currentAverage) > endingVarianceLevel * currentAverage) {
+ break;
+ } else {
+ [averagesAtHeights addObjectsFromArray:nextAverages];
+ checkHeight = nextHeight;
+ }
+ } else {
+ [averagesAtHeights addObject:@(averageValue)];
+ currentAverage = [[averagesAtHeights valueForKeyPath:@"@avg.self"] floatValue];
+ checkHeight += 500;
+ }
+ } else {
+ [averagesAtHeights addObject:@(averageValue)];
+ currentAverage = [[averagesAtHeights valueForKeyPath:@"@avg.self"] floatValue];
+ checkHeight += 500;
+ }
+ i++;
+ }
+ if (rAverage) {
+ *rAverage = currentAverage;
+ }
+ if (rAverages) {
+ *rAverages = averagesAtHeights;
+ }
+ return checkHeight;
+}
+
+- (uint16_t)averageTransactionsFor500RangeAtHeight:(uint32_t)height {
+ if (height < self.maxTransactionsInfoDataFirstHeight) return 0;
+ if (height > self.maxTransactionsInfoDataFirstHeight + self.maxTransactionsInfoData.length * 500 / 6) return 0;
+ uint32_t offset = floor(((double)height - self.maxTransactionsInfoDataFirstHeight) * 2.0 / 500.0) * 3;
+ //uint32_t checkHeight = [self.maxTransactionsInfoData UInt16AtOffset:offset]*500;
+ uint16_t average = [self.maxTransactionsInfoData UInt16AtOffset:offset + 2];
+ uint16_t max = [self.maxTransactionsInfoData UInt16AtOffset:offset + 4];
+ NSAssert(average < max, @"Sanity check that average < max");
+ return average;
+}
+
+- (uint16_t)maxTransactionsFor500RangeAtHeight:(uint32_t)height {
+ if (height < self.maxTransactionsInfoDataFirstHeight) return 0;
+ if (height > self.maxTransactionsInfoDataFirstHeight + self.maxTransactionsInfoData.length * 500 / 6) return 0;
+ uint32_t offset = floor(((double)height - self.maxTransactionsInfoDataFirstHeight) * 2.0 / 500.0) * 3;
+ //uint32_t checkHeight = [self.maxTransactionsInfoData UInt16AtOffset:offset]*500;
+ uint16_t average = [self.maxTransactionsInfoData UInt16AtOffset:offset + 2];
+ uint16_t max = [self.maxTransactionsInfoData UInt16AtOffset:offset + 4];
+ NSAssert(average < max, @"Sanity check that average < max");
+ return max;
+}
+
+- (BOOL)shouldRequestMerkleBlocksForZoneBetweenHeight:(uint32_t)blockHeight andEndHeight:(uint32_t)endBlockHeight {
+ uint16_t blockZone = blockHeight / 500;
+ uint16_t endBlockZone = endBlockHeight / 500 + (endBlockHeight % 500 ? 1 : 0);
+ if (self.chainSynchronizationFingerprint) {
+ while (blockZone < endBlockZone) {
+ if ([[self chainSynchronizationBlockZones] containsObject:@(blockZone)]) return TRUE;
+ }
+ return NO;
+ } else {
+ return YES;
+ }
+}
+
+- (BOOL)shouldRequestMerkleBlocksForZoneAfterHeight:(uint32_t)blockHeight {
+ uint16_t blockZone = blockHeight / 500;
+ uint16_t leftOver = blockHeight % 500;
+ if (self.chainSynchronizationFingerprint) {
+ return [[self chainSynchronizationBlockZones] containsObject:@(blockZone)] || [[self chainSynchronizationBlockZones] containsObject:@(blockZone + 1)] || [[self chainSynchronizationBlockZones] containsObject:@(blockZone + 2)] || [[self chainSynchronizationBlockZones] containsObject:@(blockZone + 3)] || (!leftOver && [self shouldRequestMerkleBlocksForZoneAfterHeight:(blockZone + 1) * 500]);
+ } else {
+ return YES;
+ }
+}
+
+@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h
index 500977193..0d0ba4bbe 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.h
@@ -23,9 +23,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#import "DSBackgroundManager.h"
#import "DSChain.h"
#import "DSKeyManager.h"
#import "DSPeer.h"
+#import "DSSyncState.h"
#import
NS_ASSUME_NONNULL_BEGIN
@@ -43,26 +45,22 @@ typedef NS_ENUM(uint32_t, DSSyncCountInfo)
FOUNDATION_EXPORT NSString *const DSChainManagerNotificationChainKey;
FOUNDATION_EXPORT NSString *const DSChainManagerNotificationWalletKey;
FOUNDATION_EXPORT NSString *const DSChainManagerNotificationAccountKey;
+FOUNDATION_EXPORT NSString *const DSChainManagerNotificationSyncStateKey;
FOUNDATION_EXPORT NSString *_Nonnull const DSChainManagerSyncWillStartNotification;
FOUNDATION_EXPORT NSString *_Nonnull const DSChainManagerChainSyncDidStartNotification;
-FOUNDATION_EXPORT NSString *_Nonnull const DSChainManagerSyncParametersUpdatedNotification;
FOUNDATION_EXPORT NSString *_Nonnull const DSChainManagerSyncFinishedNotification;
FOUNDATION_EXPORT NSString *_Nonnull const DSChainManagerSyncFailedNotification;
+FOUNDATION_EXPORT NSString *_Nonnull const DSChainManagerSyncStateDidChangeNotification;
-@class DSGovernanceSyncManager, DSMasternodeManager, DSSporkManager, DSPeerManager, DSGovernanceVote, DSDAPIClient, DSTransactionManager, DSIdentitiesManager, DSBloomFilter, DSBlock, DSFullBlock, DSKeyManager;
+@class DSGovernanceSyncManager, DSMasternodeManager, DSSporkManager, DSPeerManager, DSGovernanceVote, DSDAPIClient, DSTransactionManager, DSIdentitiesManager, DSBackgroundManager, DSBloomFilter, DSBlock, DSFullBlock, DSKeyManager, DSSyncState;
typedef void (^BlockMiningCompletionBlock)(DSFullBlock *_Nullable block, NSUInteger attempts, NSTimeInterval timeUsed, NSError *_Nullable error);
typedef void (^MultipleBlockMiningCompletionBlock)(NSArray *block, NSArray *attempts, NSTimeInterval timeUsed, NSError *_Nullable error);
@interface DSChainManager : NSObject
-@property (nonatomic, readonly) double chainSyncProgress;
-@property (nonatomic, readonly) double terminalHeaderSyncProgress;
-@property (nonatomic, readonly) double combinedSyncProgress;
-@property (nonatomic, readonly) double chainSyncWeight;
-@property (nonatomic, readonly) double terminalHeaderSyncWeight;
-@property (nonatomic, readonly) double masternodeListSyncWeight;
+@property (nonatomic, readonly) DSBackgroundManager *backgroundManager;
@property (nonatomic, readonly) DSSporkManager *sporkManager;
@property (nonatomic, readonly) DSMasternodeManager *masternodeManager;
@property (nonatomic, readonly) DSGovernanceSyncManager *governanceSyncManager;
@@ -73,29 +71,21 @@ typedef void (^MultipleBlockMiningCompletionBlock)(NSArray *block
@property (nonatomic, readonly) DSKeyManager *keyManager;
@property (nonatomic, readonly) DSChain *chain;
@property (nonatomic, readonly) NSData *chainSynchronizationFingerprint;
+@property (nonatomic, readonly, getter = isSynced) BOOL synced;
+@property (nonatomic, readonly) double combinedSyncProgress;
/*! @brief Returns the sync phase that the chain is currently in. */
@property (nonatomic, readonly) DSChainSyncPhase syncPhase;
-- (void)startSync;
+/*! @brief Returns determined chain sync state. */
+@property (nonatomic, readonly) DSSyncState *syncState;
+- (void)startSync;
- (void)stopSync;
-
- (void)syncBlocksRescan;
-
- (void)masternodeListAndBlocksRescan;
-
- (void)masternodeListRescan;
-// MARK: - Mining
-
-- (void)mineEmptyBlocks:(uint32_t)blockCount toPaymentAddress:(NSString *)paymentAddress withTimeout:(NSTimeInterval)timeout completion:(MultipleBlockMiningCompletionBlock)completion;
-
-- (void)mineEmptyBlocks:(uint32_t)blockCount toPaymentAddress:(NSString *)paymentAddress afterBlock:(DSBlock *)block previousBlocks:(NSDictionary *)previousBlocks withTimeout:(NSTimeInterval)timeout completion:(MultipleBlockMiningCompletionBlock)completion;
-
-- (void)mineBlockToPaymentAddress:(NSString *)paymentAddress withTransactions:(NSArray *_Nullable)transactions withTimeout:(NSTimeInterval)timeout completion:(BlockMiningCompletionBlock)completion;
-
-- (void)mineBlockAfterBlock:(DSBlock *)block toPaymentAddress:(NSString *)paymentAddress withTransactions:(NSArray *_Nullable)transactions previousBlocks:(NSDictionary *)previousBlocks nonceOffset:(uint32_t)nonceOffset withTimeout:(NSTimeInterval)timeout completion:(BlockMiningCompletionBlock)completion;
- (DSChainLock * _Nullable)chainLockForBlockHash:(UInt256)blockHash;
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m
index 18830a575..2672588d6 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m
@@ -26,7 +26,9 @@
#import "DSBloomFilter.h"
#import "DSChain+Protected.h"
#import "DSChainEntity+CoreDataClass.h"
+#import "DSChainManager+Mining.h"
#import "DSChainManager+Protected.h"
+#import "DSChainManager+Transactions.h"
#import "DSCheckpoint.h"
#import "DSDerivationPath.h"
#import "DSEventManager.h"
@@ -53,10 +55,8 @@
@interface DSChainManager ()
-@property (nonatomic, assign) double chainSyncWeight;
-@property (nonatomic, assign) double terminalHeaderSyncWeight;
-@property (nonatomic, assign) double masternodeListSyncWeight;
@property (nonatomic, strong) DSChain *chain;
+@property (nonatomic, strong) DSBackgroundManager *backgroundManager;
@property (nonatomic, strong) DSSporkManager *sporkManager;
@property (nonatomic, strong) DSMasternodeManager *masternodeManager;
@property (nonatomic, strong) DSKeyManager *keyManager;
@@ -65,17 +65,13 @@ @interface DSChainManager ()
@property (nonatomic, strong) DSDAPIClient *DAPIClient;
@property (nonatomic, strong) DSTransactionManager *transactionManager;
@property (nonatomic, strong) DSPeerManager *peerManager;
-@property (nonatomic, assign) uint32_t chainSyncStartHeight;
-@property (nonatomic, assign) uint32_t terminalSyncStartHeight;
@property (nonatomic, assign) uint64_t sessionConnectivityNonce;
@property (nonatomic, assign) BOOL gotSporksAtChainSyncStart;
-@property (nonatomic, strong) NSData *maxTransactionsInfoData;
-@property (nonatomic, strong) RHIntervalTree *heightTransactionZones;
-@property (nonatomic, assign) uint32_t maxTransactionsInfoDataFirstHeight;
-@property (nonatomic, assign) uint32_t maxTransactionsInfoDataLastHeight;
-@property (nonatomic, strong) NSData *chainSynchronizationFingerprint;
-@property (nonatomic, strong) NSOrderedSet *chainSynchronizationBlockZones;
-@property (nonatomic, strong) dispatch_queue_t miningQueue;
+
+@property (nonatomic, strong) DSSyncState *syncState;
+@property (nonatomic, assign) NSTimeInterval lastNotifiedBlockDidChange;
+@property (nonatomic, strong) NSTimer *lastNotifiedBlockDidChangeTimer;
+
@end
@@ -85,9 +81,10 @@ - (instancetype)initWithChain:(DSChain *)chain {
if (!(self = [super init])) return nil;
self.chain = chain;
- self.syncPhase = DSChainSyncPhase_Offline;
chain.chainManager = self;
+ self.syncState = [[DSSyncState alloc] initWithSyncPhase:DSChainSyncPhase_Offline];
self.keyManager = [[DSKeyManager alloc] initWithChain:chain];
+ self.backgroundManager = [[DSBackgroundManager alloc] initWithChain:chain];
self.sporkManager = [[DSSporkManager alloc] initWithChain:chain];
self.masternodeManager = [[DSMasternodeManager alloc] initWithChain:chain];
self.DAPIClient = [[DSDAPIClient alloc] initWithChain:chain]; //this must be
@@ -97,7 +94,8 @@ - (instancetype)initWithChain:(DSChain *)chain {
self.peerManager = [[DSPeerManager alloc] initWithChain:chain];
self.identitiesManager = [[DSIdentitiesManager alloc] initWithChain:chain];
self.gotSporksAtChainSyncStart = FALSE;
- self.sessionConnectivityNonce = (long long)arc4random() << 32 | arc4random();
+ self.sessionConnectivityNonce = ((uint64_t)arc4random() << 32) | arc4random();
+ self.lastNotifiedBlockDidChange = 0;
if ([self.masternodeManager hasCurrentMasternodeListInLast30Days]) {
[self.peerManager useMasternodeList:self.masternodeManager.currentMasternodeList withConnectivityNonce:self.sessionConnectivityNonce];
@@ -106,369 +104,29 @@ - (instancetype)initWithChain:(DSChain *)chain {
//[self loadMaxTransactionInfo];
//[self loadHeightTransactionZones];
- _miningQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.mining.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
+ self.miningQueue = dispatch_queue_create([[NSString stringWithFormat:@"org.dashcore.dashsync.mining.%@", self.chain.uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
DSLog(@"[%@] DSChainManager.initWithChain %@", chain.name, chain);
return self;
}
-// MARK: - Max transaction info
-
-- (void)loadMaxTransactionInfo {
- NSString *bundlePath = [[NSBundle bundleForClass:self.class] pathForResource:@"DashSync" ofType:@"bundle"];
- NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
- NSString *filePath = [bundle pathForResource:[NSString stringWithFormat:@"MaxTransactionInfo_%@", self.chain.name] ofType:@"dat"];
- self.maxTransactionsInfoData = [NSData dataWithContentsOfFile:filePath];
- if (self.maxTransactionsInfoData) {
- self.maxTransactionsInfoDataFirstHeight = [self.maxTransactionsInfoData UInt16AtOffset:0] * 500;
- self.maxTransactionsInfoDataLastHeight = [self.maxTransactionsInfoData UInt16AtOffset:self.maxTransactionsInfoData.length - 6] * 500;
- //We need MaxTransactionsInfoDataLastHeight to be after the last checkpoint so there is no gap in info. We can gather Max Transactions after the last checkpoint from the initial terminal sync.
- NSAssert(self.maxTransactionsInfoDataLastHeight > self.chain.checkpoints.lastObject.height, @"MaxTransactionsInfoDataLastHeight should always be after the last checkpoint for the system to work");
- }
-
- ////Some code to log checkpoints, keep it here for some testing in the future.
- // for (DSCheckpoint * checkpoint in self.chain.checkpoints) {
- // if (checkpoint.height > 340000) {
- // NSLog(@"%d:%d",checkpoint.height,[self averageTransactionsFor500RangeAtHeight:checkpoint.height]);
- // }
- // }
- // float average = 0;
- // uint32_t startRange = self.maxTransactionsInfoDataFirstHeight;
- // NSMutableData * data = [NSMutableData data];
- // [data appendUInt16:startRange/500];
- // while (startRange < self.maxTransactionsInfoDataLastHeight) {
- // uint32_t endRange = [self firstHeightOutOfAverageRangeWithStart500RangeHeight:startRange rAverage:&average];
- // NSLog(@"heights %d-%d averageTransactions %.1f",startRange,endRange,average);
- // startRange = endRange;
- // [data appendUInt16:(unsigned short)average];
- // [data appendUInt16:endRange/500];
- // }
- //
- // NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
- // NSString *documentsDirectory = [paths objectAtIndex:0];
- // NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"HeightTransactionZones_%@.dat",self.chain.name]];
- // [data writeToFile:dataPath atomically:YES];
- //
-}
-
-- (void)loadHeightTransactionZones {
- NSString *bundlePath = [[NSBundle bundleForClass:self.class] pathForResource:@"DashSync" ofType:@"bundle"];
- NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
- NSString *filePath = [bundle pathForResource:[NSString stringWithFormat:@"HeightTransactionZones_%@", self.chain.name] ofType:@"dat"];
- NSData *heightTransactionZonesData = [NSData dataWithContentsOfFile:filePath];
- if (heightTransactionZonesData) {
- NSMutableArray *intervals = [NSMutableArray array];
- for (uint16_t i = 0; i < heightTransactionZonesData.length - 4; i += 4) {
- uint32_t intervalStartHeight = [heightTransactionZonesData UInt16AtOffset:i] * 500;
- uint16_t average = [heightTransactionZonesData UInt16AtOffset:i + 2];
- uint32_t intervalEndHeight = [heightTransactionZonesData UInt16AtOffset:i + 4] * 500;
- [intervals addObject:[RHInterval intervalWithStart:intervalStartHeight stop:intervalEndHeight - 1 object:@(average)]];
- }
- self.heightTransactionZones = [[RHIntervalTree alloc] initWithIntervalObjects:intervals];
- }
-}
-
-- (uint16_t)averageTransactionsInZoneForStartHeight:(uint32_t)startHeight endHeight:(uint32_t)endHeight {
- NSArray *intervals = [self.heightTransactionZones overlappingObjectsForStart:startHeight andStop:endHeight];
- if (!intervals.count) return 0;
- if (intervals.count == 1) return [(NSNumber *)[intervals[0] object] unsignedShortValue];
- uint64_t aggregate = 0;
- for (RHInterval *interval in intervals) {
- uint64_t value = [(NSNumber *)interval.object unsignedLongValue];
- if (interval == [intervals firstObject]) {
- aggregate += value * (interval.stop - startHeight + 1);
- } else if (interval == [intervals lastObject]) {
- aggregate += value * (endHeight - interval.start + 1);
- } else {
- aggregate += value * (interval.stop - interval.start + 1);
- }
- }
- return aggregate / (endHeight - startHeight);
-}
-
-- (uint32_t)firstHeightOutOfAverageRangeWithStart500RangeHeight:(uint32_t)height rAverage:(float *)rAverage {
- return [self firstHeightOutOfAverageRangeWithStart500RangeHeight:height startingVarianceLevel:1 endingVarianceLevel:0.2 convergencePolynomial:0.33 rAverage:rAverage];
-}
-
-- (uint32_t)firstHeightOutOfAverageRangeWithStart500RangeHeight:(uint32_t)height startingVarianceLevel:(float)startingVarianceLevel endingVarianceLevel:(float)endingVarianceLevel convergencePolynomial:(float)convergencePolynomial rAverage:(float *)rAverage {
- return [self firstHeightOutOfAverageRangeWithStart500RangeHeight:height startingVarianceLevel:startingVarianceLevel endingVarianceLevel:endingVarianceLevel convergencePolynomial:convergencePolynomial recursionLevel:0 recursionMaxLevel:2 rAverage:rAverage rAverages:nil];
-}
-
-- (uint32_t)firstHeightOutOfAverageRangeWithStart500RangeHeight:(uint32_t)height startingVarianceLevel:(float)startingVarianceLevel endingVarianceLevel:(float)endingVarianceLevel convergencePolynomial:(float)convergencePolynomial recursionLevel:(uint16_t)recursionLevel recursionMaxLevel:(uint16_t)recursionMaxLevel rAverage:(float *)rAverage rAverages:(NSArray **)rAverages {
- NSMutableArray *averagesAtHeights = [NSMutableArray array];
- float currentAverage = 0;
- uint32_t checkHeight = height;
- uint16_t i = 0;
- float internalVarianceParameter = ((startingVarianceLevel - endingVarianceLevel) / endingVarianceLevel);
- while (checkHeight < self.maxTransactionsInfoDataLastHeight) {
- uint16_t averageValue = [self averageTransactionsFor500RangeAtHeight:checkHeight];
-
- if (i != 0 && averageValue > 10) { //before 12 just ignore
- float maxVariance = endingVarianceLevel * (powf((float)i, convergencePolynomial) + internalVarianceParameter) / powf((float)i, convergencePolynomial);
- //NSLog(@"height %d averageValue %hu currentAverage %.2f variance %.2f",checkHeight,averageValue,currentAverage,fabsf(averageValue - currentAverage)/currentAverage);
- if (fabsf(averageValue - currentAverage) > maxVariance * currentAverage) {
- //there was a big change in variance
- if (recursionLevel > recursionMaxLevel) break; //don't recurse again
- //We need to make sure that this wasn't a 1 time variance
- float nextAverage = 0;
- NSArray *nextAverages = nil;
-
- uint32_t nextHeight = [self firstHeightOutOfAverageRangeWithStart500RangeHeight:checkHeight startingVarianceLevel:startingVarianceLevel endingVarianceLevel:endingVarianceLevel convergencePolynomial:convergencePolynomial recursionLevel:recursionLevel + 1 recursionMaxLevel:recursionMaxLevel rAverage:&nextAverage rAverages:&nextAverages];
- if (fabsf(nextAverage - currentAverage) > endingVarianceLevel * currentAverage) {
- break;
- } else {
- [averagesAtHeights addObjectsFromArray:nextAverages];
- checkHeight = nextHeight;
- }
- } else {
- [averagesAtHeights addObject:@(averageValue)];
- currentAverage = [[averagesAtHeights valueForKeyPath:@"@avg.self"] floatValue];
- checkHeight += 500;
- }
- } else {
- [averagesAtHeights addObject:@(averageValue)];
- currentAverage = [[averagesAtHeights valueForKeyPath:@"@avg.self"] floatValue];
- checkHeight += 500;
- }
- i++;
- }
- if (rAverage) {
- *rAverage = currentAverage;
- }
- if (rAverages) {
- *rAverages = averagesAtHeights;
- }
- return checkHeight;
-}
-
-- (uint16_t)averageTransactionsFor500RangeAtHeight:(uint32_t)height {
- if (height < self.maxTransactionsInfoDataFirstHeight) return 0;
- if (height > self.maxTransactionsInfoDataFirstHeight + self.maxTransactionsInfoData.length * 500 / 6) return 0;
- uint32_t offset = floor(((double)height - self.maxTransactionsInfoDataFirstHeight) * 2.0 / 500.0) * 3;
- //uint32_t checkHeight = [self.maxTransactionsInfoData UInt16AtOffset:offset]*500;
- uint16_t average = [self.maxTransactionsInfoData UInt16AtOffset:offset + 2];
- uint16_t max = [self.maxTransactionsInfoData UInt16AtOffset:offset + 4];
- NSAssert(average < max, @"Sanity check that average < max");
- return average;
-}
-
-- (uint16_t)maxTransactionsFor500RangeAtHeight:(uint32_t)height {
- if (height < self.maxTransactionsInfoDataFirstHeight) return 0;
- if (height > self.maxTransactionsInfoDataFirstHeight + self.maxTransactionsInfoData.length * 500 / 6) return 0;
- uint32_t offset = floor(((double)height - self.maxTransactionsInfoDataFirstHeight) * 2.0 / 500.0) * 3;
- //uint32_t checkHeight = [self.maxTransactionsInfoData UInt16AtOffset:offset]*500;
- uint16_t average = [self.maxTransactionsInfoData UInt16AtOffset:offset + 2];
- uint16_t max = [self.maxTransactionsInfoData UInt16AtOffset:offset + 4];
- NSAssert(average < max, @"Sanity check that average < max");
- return max;
-}
-
-// MARK: - Info
-
-- (NSString *)chainSyncStartHeightKey {
- return [NSString stringWithFormat:@"%@_%@", SYNC_STARTHEIGHT_KEY, [self.chain uniqueID]];
-}
-
-- (NSString *)terminalSyncStartHeightKey {
- return [NSString stringWithFormat:@"%@_%@", TERMINAL_SYNC_STARTHEIGHT_KEY, [self.chain uniqueID]];
-}
-
-- (void)assignSyncWeights {
- uint32_t chainBlocks = [self chainBlocksToSync];
- uint32_t terminalBlocks = [self terminalHeadersToSync];
- uint32_t masternodeListsToSync = self.masternodeManager.estimatedMasternodeListsToSync;
- //a unit of weight is the time it would take to sync 1000 blocks;
- //terminal headers are 4 times faster the blocks
- //the first masternode list is worth 20000 blocks
- //each masternode list after that is worth 2000 blocks
- uint32_t chainWeight = chainBlocks;
- uint32_t terminalWeight = terminalBlocks / 4;
- uint32_t masternodeWeight = masternodeListsToSync ? (20000 + 2000 * (masternodeListsToSync - 1)) : 0;
- uint32_t totalWeight = chainWeight + terminalWeight + masternodeWeight;
- if (totalWeight == 0) {
- self.terminalHeaderSyncWeight = 0;
- self.masternodeListSyncWeight = 0;
- self.chainSyncWeight = 1;
- } else {
- self.chainSyncWeight = ((float)chainWeight) / totalWeight;
- self.terminalHeaderSyncWeight = ((float)terminalWeight) / totalWeight;
- self.masternodeListSyncWeight = ((float)masternodeWeight) / totalWeight;
- }
-}
-
-- (uint32_t)chainBlocksToSync {
- if (self.chain.lastSyncBlockHeight >= self.chain.estimatedBlockHeight) return 0;
- return self.chain.estimatedBlockHeight - self.chain.lastSyncBlockHeight;
-}
-
-- (double)chainSyncProgress {
- if (!self.peerManager.downloadPeer && self.chainSyncStartHeight == 0) return 0.0;
- if (self.chain.lastSyncBlockHeight >= self.chain.estimatedBlockHeight) return 1.0;
-
- double lastBlockHeight = self.chain.lastSyncBlockHeight;
- double estimatedBlockHeight = self.chain.estimatedBlockHeight;
- double syncStartHeight = self.chainSyncStartHeight;
- double progress;
- if (estimatedBlockHeight == 0) return 0;
- if (syncStartHeight > lastBlockHeight) {
- progress = lastBlockHeight / estimatedBlockHeight;
- } else {
- if (estimatedBlockHeight - syncStartHeight == 0) return 0;
- progress = (lastBlockHeight - syncStartHeight) / (estimatedBlockHeight - syncStartHeight);
- }
- return MIN(1.0, MAX(0.0, 0.1 + 0.9 * progress));
+- (BOOL)isSynced {
+ return self.syncState.combinedSyncProgress == 1.0;
}
-
-- (uint32_t)terminalHeadersToSync {
- if (self.chain.lastTerminalBlockHeight >= self.chain.estimatedBlockHeight) return 0;
- return self.chain.estimatedBlockHeight - self.chain.lastTerminalBlockHeight;
-}
-
-- (double)terminalHeaderSyncProgress {
- if (!self.peerManager.downloadPeer && self.terminalSyncStartHeight == 0) return 0.0;
- if (self.chain.lastTerminalBlockHeight >= self.chain.estimatedBlockHeight) return 1.0;
-
- double lastBlockHeight = self.chain.lastTerminalBlockHeight;
- double estimatedBlockHeight = self.chain.estimatedBlockHeight;
- double syncStartHeight = self.terminalSyncStartHeight;
- double progress;
- if (syncStartHeight > lastBlockHeight) {
- progress = lastBlockHeight / estimatedBlockHeight;
- } else {
- progress = (lastBlockHeight - syncStartHeight) / (estimatedBlockHeight - syncStartHeight);
- }
- return MIN(1.0, MAX(0.0, 0.1 + 0.9 * progress));
-}
-
-#define LOG_COMBINED_SYNC_PROGRESS 0
-
- (double)combinedSyncProgress {
-#if LOG_COMBINED_SYNC_PROGRESS
- DSLog(@"[%@] combinedSyncProgress breakdown %f %f %f", self.chain.name, self.terminalHeaderSyncProgress, self.masternodeManager.masternodeListAndQuorumsSyncProgress, self.chainSyncProgress);
-#endif
- if ((self.terminalHeaderSyncWeight + self.chainSyncWeight + self.masternodeListSyncWeight) == 0) {
- @synchronized (self.peerManager) {
- return self.peerManager.connected ? 1 : 0;
- }
- } else {
- double progress = self.terminalHeaderSyncProgress * self.terminalHeaderSyncWeight + self.masternodeManager.masternodeListAndQuorumsSyncProgress * self.masternodeListSyncWeight + self.chainSyncProgress * self.chainSyncWeight;
- if (progress < 0.99995) {
- return progress;
- } else {
- return 1;
- }
- }
+ return self.syncState.combinedSyncProgress;
}
-- (void)resetChainSyncStartHeight {
- NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
- if (self.chainSyncStartHeight == 0) self.chainSyncStartHeight = (uint32_t)[userDefaults integerForKey:self.chainSyncStartHeightKey];
-
- if (self.chainSyncStartHeight == 0) {
- self.chainSyncStartHeight = self.chain.lastSyncBlockHeight;
- [[NSUserDefaults standardUserDefaults] setInteger:self.chainSyncStartHeight forKey:self.chainSyncStartHeightKey];
- }
-}
-
-- (void)restartChainSyncStartHeight {
- self.chainSyncStartHeight = 0;
- [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:self.chainSyncStartHeightKey];
-}
-
-
-- (void)resetTerminalSyncStartHeight {
- NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
- if (self.terminalSyncStartHeight == 0) self.terminalSyncStartHeight = (uint32_t)[userDefaults integerForKey:self.terminalSyncStartHeightKey];
-
- if (self.terminalSyncStartHeight == 0) {
- self.terminalSyncStartHeight = self.chain.lastTerminalBlockHeight;
- [[NSUserDefaults standardUserDefaults] setInteger:self.terminalSyncStartHeight forKey:self.terminalSyncStartHeightKey];
- }
-}
-- (void)restartTerminalSyncStartHeight {
- self.terminalSyncStartHeight = 0;
- [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:self.terminalSyncStartHeightKey];
-}
+// MARK: - Info
- (void)relayedNewItem {
self.lastChainRelayTime = [NSDate timeIntervalSince1970];
}
-- (void)resetLastRelayedItemTime {
- self.lastChainRelayTime = 0;
-}
-
-// MARK: - Mining
-
-- (void)mineEmptyBlocks:(uint32_t)blockCount toPaymentAddress:(NSString *)paymentAddress withTimeout:(NSTimeInterval)timeout completion:(MultipleBlockMiningCompletionBlock)completion {
- [self mineEmptyBlocks:blockCount toPaymentAddress:paymentAddress afterBlock:self.chain.lastTerminalBlock previousBlocks:self.chain.terminalBlocks withTimeout:timeout completion:completion];
-}
-
-- (void)mineEmptyBlocks:(uint32_t)blockCount toPaymentAddress:(NSString *)paymentAddress afterBlock:(DSBlock *)previousBlock previousBlocks:(NSDictionary *)previousBlocks withTimeout:(NSTimeInterval)timeout completion:(MultipleBlockMiningCompletionBlock)completion {
- dispatch_async(_miningQueue, ^{
- NSTimeInterval start = [[NSDate date] timeIntervalSince1970];
- NSTimeInterval end = [[[NSDate alloc] initWithTimeIntervalSinceNow:timeout] timeIntervalSince1970];
- NSMutableArray *blocksArray = [NSMutableArray array];
- NSMutableArray *attemptsArray = [NSMutableArray array];
- __block uint32_t blocksRemaining = blockCount;
- __block NSMutableDictionary *mPreviousBlocks = [previousBlocks mutableCopy];
- __block DSBlock *currentBlock = previousBlock;
- while ([[NSDate date] timeIntervalSince1970] < end && blocksRemaining > 0) {
- dispatch_semaphore_t sem = dispatch_semaphore_create(0);
- [self mineBlockAfterBlock:currentBlock
- toPaymentAddress:paymentAddress
- withTransactions:[NSArray array]
- previousBlocks:mPreviousBlocks
- nonceOffset:0
- withTimeout:timeout
- completion:^(DSFullBlock *_Nullable block, NSUInteger attempts, NSTimeInterval timeUsed, NSError *_Nullable error) {
- NSAssert(uint256_is_not_zero(block.blockHash), @"Block hash must not be empty");
- dispatch_semaphore_signal(sem);
- [blocksArray addObject:block];
- [mPreviousBlocks setObject:block forKey:uint256_obj(block.blockHash)];
- currentBlock = block;
- blocksRemaining--;
- [attemptsArray addObject:@(attempts)];
- }];
- dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
- }
- if (completion) {
- dispatch_async(dispatch_get_main_queue(), ^{
- completion(blocksArray, attemptsArray, [[NSDate date] timeIntervalSince1970] - start, nil);
- });
- }
- });
-}
-
-- (void)mineBlockToPaymentAddress:(NSString *)paymentAddress withTransactions:(NSArray *)transactions withTimeout:(NSTimeInterval)timeout completion:(BlockMiningCompletionBlock)completion {
- [self mineBlockAfterBlock:self.chain.lastTerminalBlock toPaymentAddress:paymentAddress withTransactions:transactions previousBlocks:self.chain.terminalBlocks nonceOffset:0 withTimeout:timeout completion:completion];
-}
-
-- (void)mineBlockAfterBlock:(DSBlock *)block toPaymentAddress:(NSString *)paymentAddress withTransactions:(NSArray *)transactions previousBlocks:(NSDictionary *)previousBlocks nonceOffset:(uint32_t)nonceOffset withTimeout:(NSTimeInterval)timeout completion:(nonnull BlockMiningCompletionBlock)completion {
- DSCoinbaseTransaction *coinbaseTransaction = [[DSCoinbaseTransaction alloc] initWithCoinbaseMessage:@"From iOS" paymentAddresses:@[paymentAddress] atHeight:block.height + 1 onChain:block.chain];
- DSFullBlock *fullblock = [[DSFullBlock alloc] initWithCoinbaseTransaction:coinbaseTransaction transactions:[NSSet set] previousBlockHash:block.blockHash previousBlocks:previousBlocks timestamp:[[NSDate date] timeIntervalSince1970] height:block.height + 1 onChain:self.chain];
- uint64_t attempts = 0;
- NSDate *startTime = [NSDate date];
- if ([fullblock mineBlockAfterBlock:block withNonceOffset:nonceOffset withTimeout:timeout rAttempts:&attempts]) {
- if (completion) {
- completion(fullblock, attempts, -[startTime timeIntervalSinceNow], nil);
- }
- } else {
- if (completion) {
- NSError *error = [NSError errorWithCode:500 localizedDescriptionKey:@"A block could not be mined in the selected time interval."];
- completion(nil, attempts, -[startTime timeIntervalSinceNow], error);
- }
- }
-}
-
// MARK: - Blockchain Sync
- (void)startSync {
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncWillStartNotification
- object:nil
- userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self notify:DSChainManagerSyncWillStartNotification userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
DSLog(@"[%@] startSync -> peerManager::connect", self.chain.name);
[self.peerManager connect];
}
@@ -477,7 +135,8 @@ - (void)stopSync {
DSLog(@"[%@] stopSync (chain switch)", self.chain.name);
[self.masternodeManager stopSync];
[self.peerManager disconnect:DSDisconnectReason_ChainSwitch];
- self.syncPhase = DSChainSyncPhase_Offline;
+ self.syncState.syncPhase = DSChainSyncPhase_Offline;
+ [self notifySyncStateChanged];
}
- (void)removeNonMainnetTrustedPeer {
@@ -493,11 +152,7 @@ - (void)disconnectedMasternodeListAndBlocksRescan {
[[DashSync sharedSyncController] wipeBlockchainDataForChain:self.chain inContext:chainContext];
[self removeNonMainnetTrustedPeer];
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncWillStartNotification
- object:nil
- userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self notify:DSChainManagerSyncWillStartNotification userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
DSLog(@"[%@] disconnectedMasternodeListAndBlocksRescan -> peerManager::connect", self.chain.name);
[self.peerManager connect];
}
@@ -507,11 +162,7 @@ - (void)disconnectedMasternodeListRescan {
[[DashSync sharedSyncController] wipeMasternodeDataForChain:self.chain inContext:chainContext];
[self removeNonMainnetTrustedPeer];
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncWillStartNotification
- object:nil
- userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self notify:DSChainManagerSyncWillStartNotification userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
DSLog(@"[%@] disconnectedMasternodeListRescan -> peerManager::connect", self.chain.name);
[self.peerManager connect];
}
@@ -521,11 +172,7 @@ - (void)disconnectedSyncBlocksRescan {
[[DashSync sharedSyncController] wipeBlockchainNonTerminalDataForChain:self.chain inContext:chainContext];
[self removeNonMainnetTrustedPeer];
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncWillStartNotification
- object:nil
- userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self notify:DSChainManagerSyncWillStartNotification userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
DSLog(@"[%@] disconnectedSyncBlocksRescan -> peerManager::connect", self.chain.name);
[self.peerManager connect];
}
@@ -583,65 +230,30 @@ - (void)chainWasWiped:(DSChain *)chain {
}
- (void)chainWillStartSyncingBlockchain:(DSChain *)chain {
+ self.lastChainRelayTime = 0;
if (!self.gotSporksAtChainSyncStart) {
[self.sporkManager getSporks]; //get the sporks early on
}
}
-- (NSData *)chainSynchronizationFingerprint {
- // if (!_chainSynchronizationFingerprint) {
- // _chainSynchronizationFingerprint = @"".hexToData;
- // }
- return _chainSynchronizationFingerprint;
-}
-
-
-- (NSOrderedSet *)chainSynchronizationBlockZones {
- if (!_chainSynchronizationBlockZones) {
- _chainSynchronizationBlockZones = [DSWallet blockZonesFromChainSynchronizationFingerprint:self.chainSynchronizationFingerprint rVersion:0 rChainHeight:0];
- }
- return _chainSynchronizationBlockZones;
-}
-
-- (BOOL)shouldRequestMerkleBlocksForZoneBetweenHeight:(uint32_t)blockHeight andEndHeight:(uint32_t)endBlockHeight {
- uint16_t blockZone = blockHeight / 500;
- uint16_t endBlockZone = endBlockHeight / 500 + (endBlockHeight % 500 ? 1 : 0);
- if (self.chainSynchronizationFingerprint) {
- while (blockZone < endBlockZone) {
- if ([[self chainSynchronizationBlockZones] containsObject:@(blockZone)]) return TRUE;
- }
- return NO;
- } else {
- return YES;
- }
-}
-
-- (BOOL)shouldRequestMerkleBlocksForZoneAfterHeight:(uint32_t)blockHeight {
- uint16_t blockZone = blockHeight / 500;
- uint16_t leftOver = blockHeight % 500;
- if (self.chainSynchronizationFingerprint) {
- return [[self chainSynchronizationBlockZones] containsObject:@(blockZone)] || [[self chainSynchronizationBlockZones] containsObject:@(blockZone + 1)] || [[self chainSynchronizationBlockZones] containsObject:@(blockZone + 2)] || [[self chainSynchronizationBlockZones] containsObject:@(blockZone + 3)] || (!leftOver && [self shouldRequestMerkleBlocksForZoneAfterHeight:(blockZone + 1) * 500]);
- } else {
- return YES;
- }
+- (void)chainWillStartConnectingToPeers:(DSChain *)chain {
+
}
- (void)chainShouldStartSyncingBlockchain:(DSChain *)chain onPeer:(DSPeer *)peer {
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerChainSyncDidStartNotification
- object:nil
- userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSPeerManagerNotificationPeerKey: peer ? peer : [NSNull null]}];
- });
+ [self notify:DSChainManagerChainSyncDidStartNotification userInfo:@{
+ DSChainManagerNotificationChainKey: self.chain,
+ DSPeerManagerNotificationPeerKey: peer ? peer : [NSNull null]}];
dispatch_async(self.chain.networkingQueue, ^{
if ((self.syncPhase != DSChainSyncPhase_ChainSync && self.syncPhase != DSChainSyncPhase_Synced) && self.chain.needsInitialTerminalHeadersSync) {
//masternode list should be synced first and the masternode list is old
- self.syncPhase = DSChainSyncPhase_InitialTerminalBlocks;
+ self.syncState.syncPhase = DSChainSyncPhase_InitialTerminalBlocks;
[peer sendGetheadersMessageWithLocators:[self.chain terminalBlocksLocatorArray] andHashStop:UINT256_ZERO];
- } else if (([[DSOptionsManager sharedInstance] syncType] & DSSyncType_MasternodeList) && ((self.masternodeManager.lastMasternodeListBlockHeight < self.chain.lastTerminalBlockHeight - 8) || (self.masternodeManager.lastMasternodeListBlockHeight == UINT32_MAX))) {
- self.syncPhase = DSChainSyncPhase_InitialTerminalBlocks;
+ } else if (([[DSOptionsManager sharedInstance] syncType] & DSSyncType_MasternodeList) && [self.masternodeManager isMasternodeListOutdated]) {
+ self.syncState.syncPhase = DSChainSyncPhase_InitialTerminalBlocks;
[self.masternodeManager startSync];
} else {
- self.syncPhase = DSChainSyncPhase_ChainSync;
+ self.syncState.syncPhase = DSChainSyncPhase_ChainSync;
BOOL startingDevnetSync = [self.chain isDevnetAny] && self.chain.lastSyncBlockHeight < 5;
NSTimeInterval cutoffTime = self.chain.earliestWalletCreationTime - HEADER_WINDOW_BUFFER_TIME;
if (startingDevnetSync || (self.chain.lastSyncBlockTimestamp >= cutoffTime && [self shouldRequestMerkleBlocksForZoneAfterHeight:[self.chain lastSyncBlockHeight]])) {
@@ -650,6 +262,7 @@ - (void)chainShouldStartSyncingBlockchain:(DSChain *)chain onPeer:(DSPeer *)peer
[peer sendGetheadersMessageWithLocators:[self.chain chainSyncBlockLocatorArray] andHashStop:UINT256_ZERO];
}
}
+ [self notifySyncStateChanged];
});
}
@@ -666,8 +279,9 @@ - (void)chainFinishedSyncingInitialHeaders:(DSChain *)chain fromPeer:(DSPeer *)p
- (void)chainFinishedSyncingTransactionsAndBlocks:(DSChain *)chain fromPeer:(DSPeer *)peer onMainChain:(BOOL)onMainChain {
if (onMainChain && peer && (peer == self.peerManager.downloadPeer)) [self relayedNewItem];
DSLog(@"[%@] finished syncing", self.chain.name);
- self.chainSyncStartHeight = 0;
- self.syncPhase = DSChainSyncPhase_Synced;
+
+ self.syncState.chainSyncStartHeight = 0;
+ self.syncState.syncPhase = DSChainSyncPhase_Synced;
[self.transactionManager fetchMempoolFromNetwork];
[self.sporkManager getSporks];
[self.governanceSyncManager startGovernanceSync];
@@ -675,20 +289,31 @@ - (void)chainFinishedSyncingTransactionsAndBlocks:(DSChain *)chain fromPeer:(DSP
// make sure we care about masternode lists
[self.masternodeManager startSync];
}
+ [self notifySyncStateChanged];
+}
+
+- (DSChainSyncPhase)syncPhase {
+ return [self.syncState syncPhase];
+}
+
+- (void)setSyncPhase:(DSChainSyncPhase)syncPhase {
+ self.syncState.syncPhase = syncPhase;
}
- (void)syncBlockchain {
DSLog(@"[%@] syncBlockchain connected peers: %lu phase: %d", self.chain.name, self.peerManager.connectedPeerCount, self.syncPhase);
if (self.peerManager.connectedPeerCount == 0) {
if (self.syncPhase == DSChainSyncPhase_InitialTerminalBlocks) {
- self.syncPhase = DSChainSyncPhase_ChainSync;
+ self.syncState.syncPhase = DSChainSyncPhase_ChainSync;
+ [self notifySyncStateChanged];
}
DSLog(@"[%@] syncBlockchain -> peerManager::connect", self.chain.name);
[self.peerManager connect];
} else if (!self.peerManager.masternodeList && self.masternodeManager.currentMasternodeList) {
[self.peerManager useMasternodeList:self.masternodeManager.currentMasternodeList withConnectivityNonce:self.sessionConnectivityNonce];
} else if (self.syncPhase == DSChainSyncPhase_InitialTerminalBlocks) {
- self.syncPhase = DSChainSyncPhase_ChainSync;
+ self.syncState.syncPhase = DSChainSyncPhase_ChainSync;
+ [self notifySyncStateChanged];
[self chainShouldStartSyncingBlockchain:self.chain onPeer:self.peerManager.downloadPeer];
}
}
@@ -759,12 +384,7 @@ - (void)peer:(DSPeer *)peer relayedSyncInfo:(DSSyncCountInfo)syncCountInfo count
break;
}
case DSSyncCountInfo_GovernanceObject: {
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSGovernanceObjectCountUpdateNotification
- object:nil
- userInfo:@{@(syncCountInfo): @(count),
- DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self notify:DSGovernanceObjectCountUpdateNotification userInfo:@{@(syncCountInfo): @(count), DSChainManagerNotificationChainKey: self.chain}];
break;
}
case DSSyncCountInfo_GovernanceObjectVote: {
@@ -775,12 +395,7 @@ - (void)peer:(DSPeer *)peer relayedSyncInfo:(DSSyncCountInfo)syncCountInfo count
peer.governanceRequestState = DSGovernanceRequestState_GovernanceObjectVotes;
[self.governanceSyncManager finishedGovernanceVoteSyncWithPeer:peer];
} else {
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSGovernanceVoteCountUpdateNotification
- object:nil
- userInfo:@{@(syncCountInfo): @(count),
- DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self notify:DSGovernanceVoteCountUpdateNotification userInfo:@{@(syncCountInfo): @(count), DSChainManagerNotificationChainKey: self.chain}];
}
}
@@ -800,4 +415,93 @@ - (DSChainLock * _Nullable)chainLockForBlockHash:(UInt256)blockHash {
return [self.transactionManager chainLockForBlockHash:blockHash];
}
+- (NSString *)chainSyncStartHeightKey {
+ return [NSString stringWithFormat:@"%@_%@", SYNC_STARTHEIGHT_KEY, [self.chain uniqueID]];
+}
+
+- (NSString *)terminalSyncStartHeightKey {
+ return [NSString stringWithFormat:@"%@_%@", TERMINAL_SYNC_STARTHEIGHT_KEY, [self.chain uniqueID]];
+}
+
+
+- (void)resetChainSyncStartHeight {
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+ BOOL changed = NO;
+ if (self.syncState.chainSyncStartHeight == 0) {
+ self.syncState.chainSyncStartHeight = (uint32_t)[userDefaults integerForKey:self.chainSyncStartHeightKey];
+ changed = YES;
+ }
+ if (self.syncState.chainSyncStartHeight == 0) {
+ self.syncState.chainSyncStartHeight = self.chain.lastSyncBlockHeight;
+ changed = YES;
+ [[NSUserDefaults standardUserDefaults] setInteger:self.syncState.chainSyncStartHeight forKey:self.chainSyncStartHeightKey];
+ }
+ if (changed)
+ [self notifySyncStateChanged];
+}
+
+- (void)restartChainSyncStartHeight {
+ self.syncState.chainSyncStartHeight = 0;
+ [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:self.chainSyncStartHeightKey];
+ [self notifySyncStateChanged];
+
+}
+
+
+- (void)resetTerminalSyncStartHeight {
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+ if (self.syncState.terminalSyncStartHeight == 0)
+ self.syncState.terminalSyncStartHeight = (uint32_t)[userDefaults integerForKey:self.terminalSyncStartHeightKey];
+
+ if (self.syncState.terminalSyncStartHeight == 0) {
+ self.syncState.terminalSyncStartHeight = self.chain.lastTerminalBlockHeight;
+ [[NSUserDefaults standardUserDefaults] setInteger:self.syncState.terminalSyncStartHeight forKey:self.terminalSyncStartHeightKey];
+ }
+}
+
+- (void)restartTerminalSyncStartHeight {
+ self.syncState.terminalSyncStartHeight = 0;
+ [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:self.terminalSyncStartHeightKey];
+}
+
+
+// MARK: Notifications
+
+- (void)setupNotificationTimer:(void (^ __nullable)(void))completion {
+ //we should avoid dispatching this message too frequently
+ NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
+ if (!self.lastNotifiedBlockDidChange || (timestamp - self.lastNotifiedBlockDidChange > 0.1)) {
+ self.lastNotifiedBlockDidChange = timestamp;
+ if (self.lastNotifiedBlockDidChangeTimer) {
+ [self.lastNotifiedBlockDidChangeTimer invalidate];
+ self.lastNotifiedBlockDidChangeTimer = nil;
+ }
+ completion();
+ } else if (!self.lastNotifiedBlockDidChangeTimer) {
+ self.lastNotifiedBlockDidChangeTimer = [NSTimer timerWithTimeInterval:1 repeats:NO block:^(NSTimer *_Nonnull timer) {
+ completion();
+ }];
+ [[NSRunLoop mainRunLoop] addTimer:self.lastNotifiedBlockDidChangeTimer forMode:NSRunLoopCommonModes];
+ }
+}
+
+- (void)notifySyncStateChanged {
+ [self setupNotificationTimer:^{
+ @synchronized (self) {
+// NSLog(@"[%@] Sync: %@", self.chain.name, self.syncState);
+ [self notify:DSChainManagerSyncStateDidChangeNotification
+ userInfo:@{
+ DSChainManagerNotificationChainKey: self.chain,
+ DSChainManagerNotificationSyncStateKey: [self.syncState copy]
+ }];
+ }
+ }];
+
+}
+
+- (void)notify:(NSNotificationName)name userInfo:(NSDictionary *_Nullable)userInfo {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:userInfo];
+ });
+}
@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m
index 108456cdc..c17220628 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+LocalMasternode.m
@@ -25,13 +25,13 @@
NSString const *localMasternodesDictionaryKey = @"localMasternodesDictionaryKey";
-@interface DSMasternodeManager (LocalMasternode)
+@interface DSMasternodeManager ()
@property (nonatomic, strong) NSMutableDictionary *localMasternodesDictionaryByRegistrationTransactionHash;
@end
@implementation DSMasternodeManager (LocalMasternode)
-@dynamic localMasternodesDictionaryByRegistrationTransactionHash;
+//@dynamic localMasternodesDictionaryByRegistrationTransactionHash;
- (void)setLocalMasternodesDictionaryByRegistrationTransactionHash:(NSMutableDictionary *)dictionary {
objc_setAssociatedObject(self, &localMasternodesDictionaryKey, dictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h
index c5b813f93..e9ab3ff59 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager+Protected.h
@@ -47,6 +47,7 @@ NS_ASSUME_NONNULL_BEGIN
- (DSLocalMasternode *)localMasternodeFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry claimedWithOwnerWallet:(DSWallet *)wallet ownerKeyIndex:(uint32_t)ownerKeyIndex;
+ (void)saveMasternodeList:(DSMasternodeList *)masternodeList toChain:(DSChain *)chain havingModifiedMasternodes:(NSDictionary *)modifiedMasternodes createUnknownBlocks:(BOOL)createUnknownBlocks inContext:(NSManagedObjectContext *)context completion:(void (^)(NSError *error))completion;
+- (BOOL)isMasternodeListOutdated;
@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h
index 0af7fe899..6259fb70b 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.h
@@ -48,9 +48,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) NSUInteger knownMasternodeListsCount;
@property (nonatomic, readonly) uint32_t earliestMasternodeListBlockHeight;
@property (nonatomic, readonly) uint32_t lastMasternodeListBlockHeight;
-@property (nonatomic, readonly) uint32_t estimatedMasternodeListsToSync;
@property (nonatomic, readonly) DSMasternodeList *currentMasternodeList;
-@property (nonatomic, readonly) double masternodeListAndQuorumsSyncProgress;
@property (nonatomic, readonly) NSUInteger masternodeListRetrievalQueueCount;
@property (nonatomic, readonly) NSUInteger masternodeListRetrievalQueueMaxAmount;
@property (nonatomic, readonly) BOOL hasMasternodeListCurrentlyBeingSaved;
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m
index dca4860ca..92cf5bd40 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m
@@ -35,6 +35,7 @@
#import "DSMasternodeListStore+Protected.h"
#import "DSMasternodeManager+LocalMasternode.h"
#import "DSMasternodeManager+Mndiff.h"
+#import "DSMasternodeManager+Protected.h"
#import "DSMerkleBlock.h"
#import "DSMnDiffProcessingResult.h"
#import "DSOperationQueue.h"
@@ -46,7 +47,7 @@
#import "DSTransactionManager+Protected.h"
#import "NSError+Dash.h"
-#define SAVE_MASTERNODE_DIFF_TO_FILE (1 && DEBUG)
+#define SAVE_MASTERNODE_DIFF_TO_FILE (0 && DEBUG)
#define DSFullLog(FORMAT, ...) printf("%s\n", [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String])
@@ -142,6 +143,11 @@ - (uint32_t)heightForBlockHash:(UInt256)blockhash {
return [self.store heightForBlockHash:blockhash];
}
+- (BOOL)isMasternodeListOutdated {
+ uint32_t lastHeight = self.lastMasternodeListBlockHeight;
+ return lastHeight == UINT32_MAX || lastHeight < self.chain.lastTerminalBlockHeight - 8;
+}
+
- (DSSimplifiedMasternodeEntry *)masternodeHavingProviderRegistrationTransactionHash:(NSData *)providerRegistrationTransactionHash {
NSParameterAssert(providerRegistrationTransactionHash);
return [self.currentMasternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash objectForKey:providerRegistrationTransactionHash];
@@ -172,31 +178,6 @@ - (NSUInteger)masternodeListRetrievalQueueMaxAmount {
return [self.masternodeListDiffService retrievalQueueMaxAmount];
}
-- (uint32_t)estimatedMasternodeListsToSync {
- BOOL syncMasternodeLists = ([[DSOptionsManager sharedInstance] syncType] & DSSyncType_MasternodeList);
- if (!syncMasternodeLists) {
- return 0;
- }
- double amountLeft = self.masternodeListRetrievalQueueCount;
- double maxAmount = self.masternodeListRetrievalQueueMaxAmount;
- if (!maxAmount || self.store.masternodeListsByBlockHash.count <= 1) { //1 because there might be a default
- return self.store.masternodeListsToSync;
- }
- return amountLeft;
-}
-
-- (double)masternodeListAndQuorumsSyncProgress {
- @synchronized (self) {
- double amountLeft = self.masternodeListRetrievalQueueCount;
- double maxAmount = self.masternodeListRetrievalQueueMaxAmount;
- if (!amountLeft) {
- return self.store.masternodeListsAndQuorumsIsSynced;
- }
- double progress = MAX(MIN((maxAmount - amountLeft) / maxAmount, 1), 0);
- return progress;
- }
-}
-
- (BOOL)currentMasternodeListIsInLast24Hours {
if (!self.currentMasternodeList) {
return NO;
@@ -206,7 +187,6 @@ - (BOOL)currentMasternodeListIsInLast24Hours {
NSTimeInterval currentTimestamp = [[NSDate date] timeIntervalSince1970];
NSTimeInterval delta = currentTimestamp - block.timestamp;
return fabs(delta) < DAY_TIME_INTERVAL;
-
}
@@ -329,8 +309,15 @@ - (BOOL)saveCLSignature:(NSData *)blockHashData signatureData:(NSData *)signatur
- (BOOL)saveMasternodeList:(DSMasternodeList *)masternodeList forBlockHash:(UInt256)blockHash {
/// TODO: need to properly store in CoreData or wait for rust SQLite
- DSLog(@"[%@] ••• cache mnlist -> %@: %@", self.chain.name, uint256_hex(blockHash), masternodeList);
+ //DSLog(@"[%@] ••• cache mnlist -> %@: %@", self.chain.name, uint256_hex(blockHash), masternodeList);
[self.store.masternodeListsByBlockHash setObject:masternodeList forKey:uint256_data(blockHash)];
+ uint32_t lastHeight = self.lastMasternodeListBlockHeight;
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = lastHeight;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = self.store.masternodeListsByBlockHash.count;
+ DSLog(@"[%@] [DSMasternodeManager] New List Stored: %u/%lu", self.chain.name, lastHeight, self.store.masternodeListsByBlockHash.count);
+ [self.chain.chainManager notifySyncStateChanged];
+ }
return YES;
}
@@ -765,10 +752,10 @@ - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message {
return lastBlock.merkleRoot;
}];
[self processMasternodeDiffWith:message context:ctx completion:^(DSMnDiffProcessingResult * _Nonnull result) {
+ #if SAVE_MASTERNODE_DIFF_TO_FILE
UInt256 baseBlockHash = result.baseBlockHash;
UInt256 blockHash = result.blockHash;
DSLog(@"[%@] •••• -> processed mnlistdiff %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:baseBlockHash], [self heightForBlockHash:blockHash], uint256_hex(baseBlockHash), uint256_hex(blockHash));
- #if SAVE_MASTERNODE_DIFF_TO_FILE
NSString *fileName = [NSString stringWithFormat:@"MNL_%@_%@__%d.dat", @([self heightForBlockHash:baseBlockHash]), @([self heightForBlockHash:blockHash]), peer.version];
DSLog(@"[%@] •-• File %@ saved", self.chain.name, fileName);
[message saveToFile:fileName inDirectory:NSCachesDirectory];
@@ -808,6 +795,7 @@ - (void)peer:(DSPeer *)peer relayedQuorumRotationInfoMessage:(NSData *)message {
dispatch_group_leave(self.processingGroup);
return;
}
+ #if SAVE_MASTERNODE_DIFF_TO_FILE
UInt256 baseBlockHash = result.mnListDiffResultAtTip.baseBlockHash;
UInt256 blockHash = result.mnListDiffResultAtTip.blockHash;
DSLog(@"[%@] •••• -> processed qrinfo tip %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:baseBlockHash], [self heightForBlockHash:blockHash], uint256_hex(baseBlockHash), uint256_hex(blockHash));
@@ -818,7 +806,6 @@ - (void)peer:(DSPeer *)peer relayedQuorumRotationInfoMessage:(NSData *)message {
if (result.extraShare) {
DSLog(@"[%@] •••• -> processed qrinfo h-4c %u..%u %@ .. %@", self.chain.name, [self heightForBlockHash:result.mnListDiffResultAtH4C.baseBlockHash], [self heightForBlockHash:result.mnListDiffResultAtH4C.blockHash], uint256_hex(result.mnListDiffResultAtH4C.baseBlockHash), uint256_hex(result.mnListDiffResultAtH4C.blockHash));
}
- #if SAVE_MASTERNODE_DIFF_TO_FILE
NSString *fileName = [NSString stringWithFormat:@"QRINFO_%@_%@__%d.dat", @([self heightForBlockHash:baseBlockHash]), @([self heightForBlockHash:blockHash]), peer.version];
DSLog(@"[%@] •-• File %@ saved", self.chain.name, fileName);
[message saveToFile:fileName inDirectory:NSCachesDirectory];
@@ -914,4 +901,5 @@ - (UInt256)buildLLMQHashFor:(DSQuorumEntry *)quorum {
return [DSKeyManager NSDataFrom:quorum_build_llmq_hash(quorum.llmqType, quorum.quorumHash.u8)].UInt256;
}
}
+
@end
diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h
index b3b7189b2..5852a45d1 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager+Protected.h
@@ -59,6 +59,8 @@ typedef NS_ENUM(NSUInteger, DSPeerManagerDesiredState)
- (void)clearRegisteredPeers;
- (void)registerPeerAtLocation:(UInt128)IPAddress port:(uint32_t)port dapiPort:(uint32_t)dapiPort;
+- (void)startBackgroundMode:(BOOL)performDisconnects;
+
@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 1a8a53a8c..cdfb882d0 100644
--- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m
+++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m
@@ -27,6 +27,7 @@
// THE SOFTWARE.
#import "DSAccount.h"
+#import "DSBackgroundManager.h"
#import "DSBloomFilter.h"
#import "DSChain+Protected.h"
#import "DSChainEntity+CoreDataClass.h"
@@ -44,6 +45,7 @@
#import "DSPeer.h"
#import "DSPeerEntity+CoreDataClass.h"
#import "DSPeerManager+Protected.h"
+#import "DSSyncState.h"
#import "DSSpork.h"
#import "DSSporkManager.h"
#import "DSTransaction.h"
@@ -70,7 +72,7 @@
#define TESTNET_MAIN_PEER @"" //@"52.36.64.148:19999"
-#define FIXED_PEERS @"FixedPeers"
+#define FIXED_PEERS @"MainnetFixedPeers"
#define TESTNET_FIXED_PEERS @"TestnetFixedPeers"
#define SYNC_COUNT_INFO @"SYNC_COUNT_INFO"
@@ -83,17 +85,13 @@ @interface DSPeerManager ()
@property (nonatomic, strong) DSPeer *downloadPeer, *fixedPeer;
@property (nonatomic, assign) NSUInteger connectFailures, misbehavingCount, maxConnectCount;
-@property (nonatomic, strong) id backgroundObserver, walletAddedObserver;
+@property (nonatomic, strong) id walletAddedObserver;
@property (nonatomic, strong) DSChain *chain;
@property (nonatomic, assign) DSPeerManagerDesiredState desiredState;
@property (nonatomic, assign) uint64_t masternodeListConnectivityNonce;
@property (nonatomic, strong) DSMasternodeList *masternodeList;
@property (nonatomic, readonly) dispatch_queue_t networkingQueue;
-#if TARGET_OS_IOS
-@property (nonatomic, assign) NSUInteger terminalHeadersSaveTaskId, blockLocatorsSaveTaskId;
-#endif
-
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@end
@@ -111,27 +109,6 @@ - (instancetype)initWithChain:(DSChain *)chain {
self.maxConnectCount = PEER_MAX_CONNECTIONS;
-#if TARGET_OS_IOS
- self.terminalHeadersSaveTaskId = UIBackgroundTaskInvalid;
-
- self.backgroundObserver =
- [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification
- object:nil
- queue:nil
- usingBlock:^(NSNotification *note) {
- dispatch_async(self.networkingQueue, ^{
- [self savePeers];
- [self.chain saveTerminalBlocks];
- });
- if (self.terminalHeadersSaveTaskId == UIBackgroundTaskInvalid) {
- self.misbehavingCount = 0;
- dispatch_async(self.networkingQueue, ^{
- [self.connectedPeers makeObjectsPerformSelector:@selector(disconnect)];
- });
- }
- }];
-#endif
-
self.walletAddedObserver =
[[NSNotificationCenter defaultCenter] addObserverForName:DSChainWalletsDidChangeNotification
object:nil
@@ -149,7 +126,6 @@ - (instancetype)initWithChain:(DSChain *)chain {
- (void)dealloc {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
- if (self.backgroundObserver) [[NSNotificationCenter defaultCenter] removeObserver:self.backgroundObserver];
if (self.walletAddedObserver) [[NSNotificationCenter defaultCenter] removeObserver:self.walletAddedObserver];
}
@@ -190,6 +166,9 @@ - (DSSporkManager *)sporkManager {
- (DSChainManager *)chainManager {
return self.chain.chainManager;
}
+- (DSBackgroundManager *)backgroundManager {
+ return self.chain.chainManager.backgroundManager;
+}
// MARK: - Info
@@ -694,6 +673,23 @@ - (void)useMasternodeList:(DSMasternodeList *)masternodeList withConnectivityNon
});
}
+// MARK: - Background Ops (iOS)
+
+- (void)startBackgroundMode:(BOOL)performDisconnects {
+ dispatch_async(self.networkingQueue, ^{
+ [self savePeers];
+ [self.chain saveTerminalBlocks];
+ });
+ if (performDisconnects) {
+ self.misbehavingCount = 0;
+ dispatch_async(self.networkingQueue, ^{
+ [self.connectedPeers makeObjectsPerformSelector:@selector(disconnect)];
+ });
+ }
+}
+
+
+
// MARK: - Connectivity
- (void)connect {
@@ -702,55 +698,34 @@ - (void)connect {
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.connectFailures >= MAX_CONNECT_FAILURES) self.connectFailures = 0; // this attempt is a manual retry
+
@synchronized (self.chainManager) {
- if (self.chainManager.terminalHeaderSyncProgress < 1.0) {
+ if (self.chainManager.syncState.terminalHeaderSyncProgress < 1.0) {
[self.chainManager resetTerminalSyncStartHeight];
- #if TARGET_OS_IOS
- if (self.blockLocatorsSaveTaskId == UIBackgroundTaskInvalid) { // start a background task for the chain sync
- self.blockLocatorsSaveTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
- dispatch_async(self.networkingQueue, ^{
- [self.chain saveBlockLocators];
- });
-
- [self chainSyncStopped];
- }];
- }
- #endif
+ [self.backgroundManager createBlockLocatorsTask:^{
+ dispatch_async(self.networkingQueue, ^{ [self.chain saveBlockLocators]; });
+ [self chainSyncStopped];
+ }];
}
- if (self.chainManager.chainSyncProgress < 1.0) {
+ if (self.chainManager.syncState.chainSyncProgress < 1.0) {
[self.chainManager resetChainSyncStartHeight];
- #if TARGET_OS_IOS
- if (self.terminalHeadersSaveTaskId == UIBackgroundTaskInvalid) { // start a background task for the chain sync
- self.terminalHeadersSaveTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
- dispatch_async(self.networkingQueue, ^{
- [self.chain saveTerminalBlocks];
- });
-
- [self chainSyncStopped];
- }];
- }
- #endif
+ [self.backgroundManager createTerminalHeadersTask:^{
+ dispatch_async(self.networkingQueue, ^{ [self.chain saveTerminalBlocks]; });
+ [self chainSyncStopped];
+ }];
}
}
-// @synchronized(self.mutableConnectedPeers) {
-// [self.mutableConnectedPeers minusSet:[self.connectedPeers objectsPassingTest:^BOOL(id obj, BOOL *stop) {
-// return ([obj status] == DSPeerStatus_Disconnected) ? YES : NO;
-// }]];
-// }
@synchronized(self.mutableConnectedPeers) {
NSMutableSet *disconnectedPeers = [NSMutableSet set];
for (DSPeer *peer in self.mutableConnectedPeers) {
-// @synchronized(peer) {
- if (peer.status == DSPeerStatus_Disconnected) {
- [disconnectedPeers addObject:peer];
- }
-// }
+ if (peer.status == DSPeerStatus_Disconnected) {
+ [disconnectedPeers addObject:peer];
+ }
}
[self.mutableConnectedPeers minusSet:disconnectedPeers];
}
-
self.fixedPeer = [self trustedPeerHost] ? [DSPeer peerWithHost:[self trustedPeerHost] onChain:self.chain] : nil;
self.maxConnectCount = (self.fixedPeer) ? 1 : PEER_MAX_CONNECTIONS;
if (self.connectedPeers.count >= self.maxConnectCount) return; // already connected to maxConnectCount peers
@@ -768,7 +743,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.chainManager peerDelegate:self transactionDelegate:self.transactionManager governanceDelegate:self.governanceSyncManager sporkDelegate:self.sporkManager masternodeDelegate:self.masternodeManager queue:self.networkingQueue];
peer.earliestKeyTime = earliestWalletCreationTime;
[self.mutableConnectedPeers addObject:peer];
@@ -785,6 +760,7 @@ - (void)connect {
if (peers.count == 0) {
[self chainSyncStopped];
+ DSLog(@"[%@] [DSPeerManager] No peers found -> SyncFailed", self.chain.name);
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = [NSError errorWithCode:1 localizedDescriptionKey:@"No peers found"];
[[NSNotificationCenter defaultCenter] postNotificationName:DSChainManagerSyncFailedNotification
@@ -817,35 +793,30 @@ - (void)disconnectDownloadPeerForError:(NSError *)error withCompletion:(void (^_
}
- (void)syncTimeout {
- @synchronized (self.chainManager) {
+ dispatch_async(self.networkingQueue, ^{
NSTimeInterval now = [NSDate timeIntervalSince1970];
NSTimeInterval delta = now - self.chainManager.lastChainRelayTime;
if (delta < PROTOCOL_TIMEOUT) { // the download peer relayed something in time, so restart timer
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(syncTimeout) object:nil];
- [self performSelector:@selector(syncTimeout)
- withObject:nil
- afterDelay:PROTOCOL_TIMEOUT - delta];
+ [self restartSyncTimeout:PROTOCOL_TIMEOUT - delta];
return;
}
- }
+ });
[self disconnectDownloadPeerForError:[NSError errorWithCode:500 descriptionKey:DSLocalizedString(@"Synchronization Timeout", @"An error message for notifying that chain sync has timed out")] withCompletion:nil];
}
+- (void)restartSyncTimeout:(NSTimeInterval)afterDelay {
+ [self cancelSyncTimeout];
+ [self performSelector:@selector(syncTimeout) withObject:nil afterDelay:afterDelay];
+}
+
+- (void)cancelSyncTimeout {
+ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(syncTimeout) object:nil];
+}
- (void)chainSyncStopped {
dispatch_async(dispatch_get_main_queue(), ^{
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(syncTimeout) object:nil];
-#if TARGET_OS_IOS
- if (self.terminalHeadersSaveTaskId != UIBackgroundTaskInvalid) {
- [[UIApplication sharedApplication] endBackgroundTask:self.terminalHeadersSaveTaskId];
- self.terminalHeadersSaveTaskId = UIBackgroundTaskInvalid;
- }
-
- if (self.blockLocatorsSaveTaskId != UIBackgroundTaskInvalid) {
- [[UIApplication sharedApplication] endBackgroundTask:self.blockLocatorsSaveTaskId];
- self.blockLocatorsSaveTaskId = UIBackgroundTaskInvalid;
- }
-#endif
+ [self cancelSyncTimeout];
+ [self.backgroundManager stopBackgroundActivities];
});
}
@@ -910,7 +881,9 @@ - (void)peerConnected:(DSPeer *)peer {
[peer sendGetaddrMessage]; // request a list of other dash peers
}
dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
+ [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification
+ object:nil
+ userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
});
}];
}];
@@ -949,24 +922,31 @@ - (void)peerConnected:(DSPeer *)peer {
@synchronized (self) {
_connected = YES;
}
+
if ([self.chain syncsBlockchain] && [self.chain canConstructAFilter]) {
[bestPeer sendFilterloadMessage:[self.transactionManager transactionsBloomFilterForPeer:bestPeer].data];
}
- bestPeer.currentBlockHeight = self.chain.lastSyncBlockHeight;
+
+ uint32_t estimatedBlockHeight = self.chain.estimatedBlockHeight;
+ uint32_t lastSyncBlockHeight = self.chain.lastSyncBlockHeight;
+ uint32_t lastTerminalBlockHeight = self.chain.lastTerminalBlockHeight;
+ uint32_t bestPeerLastBlockHeight = bestPeer.lastBlockHeight;
+ bestPeer.currentBlockHeight = lastSyncBlockHeight;
dispatch_async(dispatch_get_main_queue(), ^{ // setup a timer to detect if the sync stalls
- [self.chainManager assignSyncWeights];
- if ([self.chain syncsBlockchain] &&
- ((self.chain.lastSyncBlockHeight != self.chain.lastTerminalBlockHeight) ||
- (self.chain.lastSyncBlockHeight < bestPeer.lastBlockHeight))) { // start blockchain sync
- [self.chainManager resetLastRelayedItemTime];
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(syncTimeout) object:nil];
- [self performSelector:@selector(syncTimeout) withObject:nil afterDelay:PROTOCOL_TIMEOUT];
-
- [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
-
- [self.chainManager chainWillStartSyncingBlockchain:self.chain];
- [self.chainManager chainShouldStartSyncingBlockchain:self.chain onPeer:bestPeer];
+ self.chainManager.syncState.hasDownloadPeer = bestPeer;
+ self.chainManager.syncState.peerManagerConnected = YES;
+ self.chainManager.syncState.estimatedBlockHeight = estimatedBlockHeight;
+ self.chainManager.syncState.lastTerminalBlockHeight = lastTerminalBlockHeight;
+ self.chainManager.syncState.lastSyncBlockHeight = lastSyncBlockHeight;
+ if ([self.chain syncsBlockchain] && ((lastSyncBlockHeight != lastTerminalBlockHeight) || (lastSyncBlockHeight < bestPeerLastBlockHeight))) {
+ // start blockchain sync
+ [self restartSyncTimeout:PROTOCOL_TIMEOUT];
+ [[NSNotificationCenter defaultCenter] postNotificationName:DSTransactionManagerTransactionStatusDidChangeNotification
+ object:nil
+ userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
+ [self.chainManager chainWillStartSyncingBlockchain:self.chain];
+ [self.chainManager chainShouldStartSyncingBlockchain:self.chain onPeer:bestPeer];
} else { // we're already synced
[self.chainManager chainFinishedSyncingTransactionsAndBlocks:self.chain fromPeer:nil onMainChain:TRUE];
}
@@ -996,6 +976,10 @@ - (void)peer:(DSPeer *)peer disconnectedWithError:(NSError *)error {
_connected = NO;
[self.chain removeEstimatedBlockHeightOfPeer:peer];
self.downloadPeer = nil;
+
+ self.chainManager.syncState.hasDownloadPeer = NO;
+ [self.chainManager notifySyncStateChanged];
+
if (self.connectFailures > MAX_CONNECT_FAILURES) self.connectFailures = MAX_CONNECT_FAILURES;
}
@@ -1025,8 +1009,7 @@ - (void)peer:(DSPeer *)peer disconnectedWithError:(NSError *)error {
} else if (self.connectFailures < MAX_CONNECT_FAILURES) {
dispatch_async(dispatch_get_main_queue(), ^{
#if TARGET_OS_IOS
- if ((self.desiredState == DSPeerManagerDesiredState_Connected) && (self.terminalHeadersSaveTaskId != UIBackgroundTaskInvalid ||
- [UIApplication sharedApplication].applicationState != UIApplicationStateBackground)) {
+ if ((self.desiredState == DSPeerManagerDesiredState_Connected) && [self.backgroundManager hasValidHeadersTask]) {
DSLog(@"[%@: %@:%d] [DSPeerManager] peer disconnectedWithError -> peerManager::connect", self.chain.name, peer.host, peer.port);
if (!banned) [self connect]; // try connecting to another peer
}
@@ -1077,4 +1060,5 @@ - (void)sendRequest:(DSMessageRequest *)request {
[self.downloadPeer sendRequest:request];
}
+
@end
diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.m b/DashSync/shared/Models/Masternode/DSMasternodeListService.m
index ef62bf210..3c8c15700 100644
--- a/DashSync/shared/Models/Masternode/DSMasternodeListService.m
+++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.m
@@ -21,8 +21,10 @@
#import "DSMasternodeListStore+Protected.h"
#import "DSChain+Protected.h"
#import "DSChainManager.h"
+#import "DSChainManager+Protected.h"
#import "DSGetMNListDiffRequest.h"
#import "DSGetQRInfoRequest.h"
+#import "DSMasternodeManager+Protected.h"
#import "DSMerkleBlock.h"
#import "DSPeerManager+Protected.h"
#import "DSSimplifiedMasternodeEntry.h"
@@ -212,7 +214,7 @@ - (BOOL)shouldProcessDiffResult:(DSMnDiffProcessingResult *)diffResult skipPrese
UInt256 masternodeListBlockHash = masternodeList.blockHash;
NSData *masternodeListBlockHashData = uint256_data(masternodeListBlockHash);
BOOL hasInRetrieval = [self.retrievalQueue containsObject:masternodeListBlockHashData];
- uint32_t masternodeListBlockHeight = [self.store heightForBlockHash:masternodeListBlockHash];
+// uint32_t masternodeListBlockHeight = [self.store heightForBlockHash:masternodeListBlockHash];
BOOL shouldNot = !hasInRetrieval && !skipPresenceInRetrieval;
//DSLog(@"•••• shouldProcessDiffResult: %d: %@ %d", masternodeListBlockHeight, uint256_reverse_hex(masternodeListBlockHash), !shouldNot);
if (shouldNot) {
@@ -256,6 +258,13 @@ - (void)addToRetrievalQueueArray:(NSArray *)masternodeBlockHashDataArr
- (void)removeFromRetrievalQueue:(NSData *)masternodeBlockHashData {
[self.retrievalQueue removeObject:masternodeBlockHashData];
+ double count = self.retrievalQueue.count;
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueCount = count;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueMaxAmount = self.retrievalQueueMaxAmount;
+ DSLog(@"[%@] Masternode list queue updated: %f/%lu", self.chain.name, count, self.retrievalQueueMaxAmount);
+ [self.chain.chainManager notifySyncStateChanged];
+ }
}
- (void)cleanRequestsInRetrieval {
@@ -264,6 +273,12 @@ - (void)cleanRequestsInRetrieval {
- (void)cleanListsRetrievalQueue {
[self.retrievalQueue removeAllObjects];
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueCount = 0;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueMaxAmount = self.retrievalQueueMaxAmount;
+ DSLog(@"[%@] Masternode list queue cleaned up: 0/%lu", self.chain.name, self.retrievalQueueMaxAmount);
+ [self.chain.chainManager notifySyncStateChanged];
+ }
}
- (void)cleanAllLists {
@@ -282,12 +297,16 @@ - (NSUInteger)retrievalQueueCount {
- (void)updateMasternodeRetrievalQueue {
NSUInteger currentCount = self.retrievalQueue.count;
- dispatch_async(dispatch_get_main_queue(), ^{
- self.retrievalQueueMaxAmount = MAX(self.retrievalQueueMaxAmount, currentCount);
- });
+ self.retrievalQueueMaxAmount = MAX(self.retrievalQueueMaxAmount, currentCount);
[self.retrievalQueue sortUsingComparator:^NSComparisonResult(NSData *_Nonnull obj1, NSData *_Nonnull obj2) {
return [self.store heightForBlockHash:obj1.UInt256] < [self.store heightForBlockHash:obj2.UInt256] ? NSOrderedAscending : NSOrderedDescending;
}];
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueCount = currentCount;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.retrievalQueueMaxAmount = self.retrievalQueueMaxAmount;
+ DSLog(@"[%@] Masternode list queue updated: %lu/%lu", self.chain.name, currentCount, self.retrievalQueueMaxAmount);
+ [self.chain.chainManager notifySyncStateChanged];
+ }
}
- (void)fetchMasternodeListsToRetrieve:(void (^)(NSOrderedSet *listsToRetrieve))completion {
@@ -378,9 +397,7 @@ - (void)issueWithMasternodeListFromPeer:(DSPeer *)peer {
[[NSUserDefaults standardUserDefaults] setObject:faultyPeers forKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS];
[self dequeueMasternodeListRequest];
}
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:DSMasternodeListDiffValidationErrorNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
- });
+ [self.chain.chainManager notify:DSMasternodeListDiffValidationErrorNotification userInfo:@{DSChainManagerNotificationChainKey: self.chain}];
}
- (void)sendMasternodeListRequest:(DSMasternodeListRequest *)request {
diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListStore.h b/DashSync/shared/Models/Masternode/DSMasternodeListStore.h
index 50092f0c8..89085e3aa 100644
--- a/DashSync/shared/Models/Masternode/DSMasternodeListStore.h
+++ b/DashSync/shared/Models/Masternode/DSMasternodeListStore.h
@@ -32,7 +32,6 @@ FOUNDATION_EXPORT NSString *const DSQuorumListDidChangeNotification;
@interface DSMasternodeListStore : NSObject
-//@property (nonatomic, nullable) DSMasternodeList *currentMasternodeList;
@property (nonatomic, readonly) NSUInteger knownMasternodeListsCount;
@property (nonatomic, readonly) NSArray *recentMasternodeLists;
@property (nonatomic, readonly) uint32_t earliestMasternodeListBlockHeight;
@@ -40,8 +39,6 @@ FOUNDATION_EXPORT NSString *const DSQuorumListDidChangeNotification;
@property (nonatomic, readonly) NSMutableDictionary *masternodeListsByBlockHash;
@property (nonatomic, readonly) NSMutableSet *masternodeListsBlockHashStubs;
@property (nonatomic, readonly) NSMutableOrderedSet *activeQuorums;
-@property (nonatomic, readonly) uint32_t masternodeListsToSync;
-@property (nonatomic, readonly) BOOL masternodeListsAndQuorumsIsSynced;
@property (nonatomic, readonly) NSMutableDictionary *cachedQuorumSnapshots;
@property (nonatomic, readonly) NSMutableDictionary *cachedCLSignatures;
diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListStore.m b/DashSync/shared/Models/Masternode/DSMasternodeListStore.m
index b886769c3..3211e1729 100644
--- a/DashSync/shared/Models/Masternode/DSMasternodeListStore.m
+++ b/DashSync/shared/Models/Masternode/DSMasternodeListStore.m
@@ -21,6 +21,7 @@
#import "DSChain+Protected.h"
#import "DSChainEntity+CoreDataProperties.h"
#import "DSChainManager.h"
+#import "DSChainManager+Protected.h"
#import "DSCheckpoint.h"
#import "DSDAPIClient.h"
#import "DSLocalMasternodeEntity+CoreDataClass.h"
@@ -218,25 +219,6 @@ - (BOOL)hasMasternodeListCurrentlyBeingSaved {
return !!self.masternodeListCurrentlyBeingSavedCount;
}
-- (uint32_t)masternodeListsToSync {
- if (self.lastMasternodeListBlockHeight == UINT32_MAX) {
- return 32;
- } else {
- float diff = self.chain.estimatedBlockHeight - self.lastMasternodeListBlockHeight;
- if (diff < 0) return 32;
- return MIN(32, (uint32_t)ceil(diff / 24.0f));
- }
-}
-
-- (BOOL)masternodeListsAndQuorumsIsSynced {
- if (self.lastMasternodeListBlockHeight == UINT32_MAX ||
- self.lastMasternodeListBlockHeight < self.chain.estimatedBlockHeight - 16) {
- return NO;
- } else {
- return YES;
- }
-}
-
- (void)loadLocalMasternodes {
NSFetchRequest *fetchRequest = [[DSLocalMasternodeEntity fetchRequest] copy];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"providerRegistrationTransaction.transactionHash.chain == %@", [self.chain chainEntityInContext:self.managedObjectContext]]];
@@ -256,12 +238,20 @@ - (DSMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash withBloc
masternodeList = [masternodeListEntity masternodeListWithSimplifiedMasternodeEntryPool:[simplifiedMasternodeEntryPool copy] quorumEntryPool:quorumEntryPool withBlockHeightLookup:blockHeightLookup];
if (masternodeList) {
DSLog(@"[%@] ••• addMasternodeList (loadMasternodeListAtBlockHash) -> %@: %@", self.chain.name, blockHash.hexString, masternodeList);
+ double count;
@synchronized (self.masternodeListsByBlockHash) {
[self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHash];
+ count = self.masternodeListsByBlockHash.count;
}
- @synchronized (self.masternodeListsByBlockHash) {
+ @synchronized (self.masternodeListsBlockHashStubs) {
[self.masternodeListsBlockHashStubs removeObject:blockHash];
}
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = count;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight;
+ [self.chain.chainManager notifySyncStateChanged];
+ [self.chain.chainManager notifySyncStateChanged];
+ }
DSLog(@"[%@] Loading Masternode List at height %u for blockHash %@ with %lu entries", self.chain.name, masternodeList.height, uint256_hex(masternodeList.blockHash), (unsigned long)masternodeList.simplifiedMasternodeEntries.count);
}
}];
@@ -285,6 +275,13 @@ - (DSMasternodeList *)loadMasternodeListsWithBlockHeightLookup:(BlockHeightFinde
//we only need a few in memory as new quorums will mostly be verified against recent masternode lists
DSMasternodeList *masternodeList = [masternodeListEntity masternodeListWithSimplifiedMasternodeEntryPool:[simplifiedMasternodeEntryPool copy] quorumEntryPool:quorumEntryPool withBlockHeightLookup:blockHeightLookup];
[self.masternodeListsByBlockHash setObject:masternodeList forKey:uint256_data(masternodeList.blockHash)];
+ double listCount = self.masternodeListsByBlockHash.count;
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = listCount;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight;
+ [self.chain.chainManager notifySyncStateChanged];
+ }
+
[self.cachedBlockHashHeights setObject:@(masternodeListEntity.block.height) forKey:uint256_data(masternodeList.blockHash)];
[simplifiedMasternodeEntryPool addEntriesFromDictionary:masternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash];
[quorumEntryPool addEntriesFromDictionary:masternodeList.quorums];
@@ -347,6 +344,12 @@ - (void)removeAllMasternodeLists {
[self.masternodeListsBlockHashStubs removeAllObjects];
}
self.masternodeListAwaitingQuorumValidation = nil;
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = UINT32_MAX;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = 0;
+ DSLog(@"[%@] [DSMasternodeManager] All List Removed: %u/%u", self.chain.name, UINT32_MAX, 0);
+ [self.chain.chainManager notifySyncStateChanged];
+ }
}
- (void)removeOldMasternodeLists:(uint32_t)lastBlockHeight {
@@ -370,6 +373,7 @@ - (void)removeOldMasternodeLists:(uint32_t)lastBlockHeight {
}
}
if (removedItems) {
+
//Now we should delete old quorums
//To do this, first get the last 24 active masternode lists
//Then check for quorums not referenced by them, and delete those
@@ -381,6 +385,12 @@ - (void)removeOldMasternodeLists:(uint32_t)lastBlockHeight {
[self.managedObjectContext deleteObject:unusedQuorumEntryEntity];
}
[self.managedObjectContext ds_save];
+
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = self.masternodeListsByBlockHash.count;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight;
+ [self.chain.chainManager notifySyncStateChanged];
+ }
}
}
}];
@@ -438,8 +448,15 @@ - (void)saveMasternodeList:(DSMasternodeList *)masternodeList addedMasternodes:(
}
NSArray *updatedSimplifiedMasternodeEntries = [addedMasternodes.allValues arrayByAddingObjectsFromArray:modifiedMasternodes.allValues];
[self.chain updateAddressUsageOfSimplifiedMasternodeEntries:updatedSimplifiedMasternodeEntries];
+ double count;
@synchronized (self.masternodeListsByBlockHash) {
[self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHashData];
+ count = self.masternodeListsByBlockHash.count;
+ }
+ @synchronized (self.chain.chainManager.syncState) {
+ self.chain.chainManager.syncState.masternodeListSyncInfo.storedCount = count;
+ self.chain.chainManager.syncState.masternodeListSyncInfo.lastBlockHeight = self.lastMasternodeListBlockHeight;
+ [self.chain.chainManager notifySyncStateChanged];
}
[self notifyMasternodeListUpdate];
dispatch_group_enter(self.savingGroup);
diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m
index 1f7f9ee61..62f4b48f2 100644
--- a/DashSync/shared/Models/Network/DSPeer.m
+++ b/DashSync/shared/Models/Network/DSPeer.m
@@ -34,6 +34,7 @@
#import "DSChainEntity+CoreDataClass.h"
#import "DSChainLock.h"
#import "DSChainManager+Protected.h"
+#import "DSChainManager+Transactions.h"
#import "DSChainManager.h"
#import "DSFilterLoadRequest.h"
#import "DSGetBlocksRequest.h"
@@ -576,9 +577,9 @@ - (void)sendGetblocksMessageWithLocators:(NSArray *)locators andHashStop:(UInt25
NSMutableArray *locatorHexes = [NSMutableArray arrayWithCapacity:[locators count]];
[locators enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
uint32_t knownHeight = [self.chain quickHeightForBlockHash:((NSData *)obj).UInt256];
- [locatorHexes addObject:[NSString stringWithFormat:@"%@ (block height %@)",
+ [locatorHexes addObject:[NSString stringWithFormat:@"%@ (block height %d)",
((NSData *)obj).reverse.hexString,
- knownHeight == UINT32_MAX ? @"unknown" : @"%d", knownHeight]];
+ knownHeight == UINT32_MAX ? 0 : knownHeight]];
}];
#if DEBUG
DSLogPrivateWithLocation(self, @"%@sending getblocks with locators %@", self.peerDelegate.downloadPeer == self ? @"(download peer) " : @"", locatorHexes);
@@ -663,7 +664,7 @@ - (void)sendGetdataMessageWithTxHashes:(NSArray *)txHashes instantSendLockHashes
if (!([[DSOptionsManager sharedInstance] syncType] & DSSyncType_GetsNewBlocks)) return;
NSUInteger totalCount = txHashes.count + instantSendLockHashes.count + instantSendLockDHashes.count + blockHashes.count + chainLockHashes.count;
if (totalCount > MAX_GETDATA_HASHES) { // limit total hash count to MAX_GETDATA_HASHES
- DSLogWithLocation(self, @"couldn't send getdata, %u is too many items, max is %u", totalCount, MAX_GETDATA_HASHES);
+ DSLogWithLocation(self, @"couldn't send getdata, %lu is too many items, max is %u", totalCount, MAX_GETDATA_HASHES);
return;
} else if (totalCount == 0)
return;
@@ -682,7 +683,7 @@ - (void)sendGetdataMessageWithTxHashes:(NSArray *)txHashes instantSendLockHashes
- (void)sendGovernanceRequest:(DSGovernanceHashesRequest *)request {
if (request.hashes.count > MAX_GETDATA_HASHES) { // limit total hash count to MAX_GETDATA_HASHES
- DSLogWithLocation(self, @"couldn't send governance votes getdata, %u is too many items, max is %u", request.hashes.count, MAX_GETDATA_HASHES);
+ DSLogWithLocation(self, @"couldn't send governance votes getdata, %lu is too many items, max is %u", request.hashes.count, MAX_GETDATA_HASHES);
return;
} else if (request.hashes.count == 0) {
DSLogWithLocation(self, @"couldn't send governance getdata, there is no items");
@@ -722,7 +723,7 @@ - (void)rerequestBlocksFrom:(UInt256)blockHash {
if (i != NSNotFound) {
[self.knownBlockHashes removeObjectsInRange:NSMakeRange(0, i)];
- DSLogWithLocation(self, @"re-requesting %u blocks", self.knownBlockHashes.count);
+ DSLogWithLocation(self, @"re-requesting %lu blocks", self.knownBlockHashes.count);
[self sendGetdataMessageWithTxHashes:nil instantSendLockHashes:nil instantSendLockDHashes:nil blockHashes:self.knownBlockHashes.array chainLockHashes:nil];
}
}
@@ -928,14 +929,14 @@ - (void)acceptAddrMessage:(NSData *)message {
NSMutableArray *peers = [NSMutableArray array];
if (count > 1000) {
- DSLogWithLocation(self, @"dropping addr message, %u is too many addresses (max 1000)", count);
+ DSLogWithLocation(self, @"dropping addr message, %lu is too many addresses (max 1000)", count);
return;
} else if (message.length < l.unsignedIntegerValue + count * 30) {
[self error:@"malformed addr message, length is %u, should be %u for %u addresses", (int)message.length,
(int)(l.unsignedIntegerValue + count * 30), (int)count];
return;
} else
- DSLogWithLocation(self, @"got addr with %u addresses", count);
+ DSLogWithLocation(self, @"got addr with %lu addresses", count);
for (NSUInteger off = l.unsignedIntegerValue; off < l.unsignedIntegerValue + 30 * count; off += 30) {
NSTimeInterval timestamp = [message UInt32AtOffset:off];
@@ -964,7 +965,7 @@ - (void)acceptAddrMessage:(NSData *)message {
}
- (void)acceptAddrV2Message:(NSData *)message {
- DSLogWithLocation(self, @"sendaddrv2, len:%u, (not implemented)", message.length);
+ DSLogWithLocation(self, @"sendaddrv2, len:%lu, (not implemented)", message.length);
}
- (NSString *)nameOfInvMessage:(DSInvType)type {
@@ -1037,7 +1038,7 @@ - (void)acceptInvMessage:(NSData *)message {
(int)(((l.unsignedIntegerValue == 0) ? 1 : l.unsignedIntegerValue) + count * 36), (int)count];
return;
} else if (count > MAX_GETDATA_HASHES) {
- DSLogWithLocation(self, @"dropping inv message, %u is too many items, max is %u", count, MAX_GETDATA_HASHES);
+ DSLogWithLocation(self, @"dropping inv message, %lu is too many items, max is %u", count, MAX_GETDATA_HASHES);
return;
}
#if MESSAGE_LOGGING
@@ -1409,7 +1410,7 @@ - (void)acceptHeadersMessage:(NSData *)message {
DSLogWithLocation(self, @"got 0 headers (%@)", @"");
#endif
} else {
- DSLogWithLocation(self, @"got %u headers", count);
+ DSLogWithLocation(self, @"got %lu headers", count);
}
#if LOG_ALL_HEADERS_IN_ACCEPT_HEADERS
@@ -1501,11 +1502,11 @@ - (void)acceptGetdataMessage:(NSData *)message {
(int)(((l == 0) ? 1 : l) + count * 36), (int)count];
return;
} else if (count > MAX_GETDATA_HASHES) {
- DSLogWithLocation(self, @"dropping getdata message, %u is too many items, max is %u", count, MAX_GETDATA_HASHES);
+ DSLogWithLocation(self, @"dropping getdata message, %lu is too many items, max is %u", count, MAX_GETDATA_HASHES);
return;
}
- DSLogWithLocation(self, @"%@got getdata for %u item%@", self.peerDelegate.downloadPeer == self ? @"(download peer)" : @"", count, count == 1 ? @"" : @"s");
+ DSLogWithLocation(self, @"%@got getdata for %lu item%@", self.peerDelegate.downloadPeer == self ? @"(download peer)" : @"", count, count == 1 ? @"" : @"s");
[self dispatchAsyncInDelegateQueue:^{
NSMutableData *notfound = [NSMutableData data];
@@ -1583,7 +1584,7 @@ - (void)acceptNotfoundMessage:(NSData *)message {
return;
}
- DSLogWithLocation(self, @"got notfound with %u item%@ (first item %@)", count, count == 1 ? @"" : @"s", [self nameOfInvMessage:[message UInt32AtOffset:l]]);
+ DSLogWithLocation(self, @"got notfound with %lu item%@ (first item %@)", count, count == 1 ? @"" : @"s", [self nameOfInvMessage:[message UInt32AtOffset:l]]);
for (NSUInteger off = l; off < l + 36 * count; off += 36) {
if ([message UInt32AtOffset:off] == DSInvType_Tx) {
@@ -1637,11 +1638,16 @@ - (void)acceptPongMessage:(NSData *)message {
DSLogWithLocation(self, @"got pong in %fs", self.pingTime);
#endif
if (self->_status == DSPeerStatus_Connected && self.pongHandlers.count) {
- void (^handler)(BOOL) = [self.pongHandlers objectAtIndex:0];
- [self.pongHandlers removeObjectAtIndex:0];
- [self dispatchAsyncInDelegateQueue:^{
- handler(YES);
- }];
+ void (^handler)(BOOL) = nil;
+ @synchronized(self.pongHandlers) {
+ if (self.pongHandlers.count > 0) {
+ handler = [self.pongHandlers objectAtIndex:0];
+ [self.pongHandlers removeObjectAtIndex:0];
+ }
+ }
+ if (handler) {
+ [self dispatchAsyncInDelegateQueue:^{ handler(YES); }];
+ }
}
}
@@ -2068,7 +2074,7 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
break;
default:
- DSLogWithLocation(self, @"unknown network stream eventCode:%u", eventCode);
+ DSLogWithLocation(self, @"unknown network stream eventCode:%lu", eventCode);
}
}
diff --git a/DashSync/shared/Models/Notifications/DSSyncState.h b/DashSync/shared/Models/Notifications/DSSyncState.h
new file mode 100644
index 000000000..b3a775d28
--- /dev/null
+++ b/DashSync/shared/Models/Notifications/DSSyncState.h
@@ -0,0 +1,69 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import
+#import "DSChain.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef NS_ENUM(uint16_t, DSSyncStateKind) {
+ DSSyncStateKind_Chain = 0,
+ DSSyncStateKind_Headers = 1,
+ DSSyncStateKind_Masternodes = 2,
+};
+
+
+@interface DSMasternodeListSyncState : NSObject
+
+@property (nonatomic, assign) uint32_t retrievalQueueCount;
+@property (nonatomic, assign) uint32_t retrievalQueueMaxAmount;
+@property (nonatomic, assign) double storedCount;
+@property (nonatomic, assign) uint32_t lastBlockHeight;
+
+@end
+
+
+@interface DSSyncState : NSObject
+
+@property (nonatomic, assign) DSChainSyncPhase syncPhase;
+@property (nonatomic, assign) BOOL hasDownloadPeer;
+@property (nonatomic, assign) BOOL peerManagerConnected;
+
+@property (nonatomic, assign) uint32_t estimatedBlockHeight;
+
+@property (nonatomic, assign) uint32_t lastSyncBlockHeight;
+@property (nonatomic, assign) uint32_t chainSyncStartHeight;
+
+@property (nonatomic, assign) uint32_t lastTerminalBlockHeight;
+@property (nonatomic, assign) uint32_t terminalSyncStartHeight;
+@property (nonatomic, strong) DSMasternodeListSyncState *masternodeListSyncInfo;
+
+// MARK: Read-only
+
+@property (nonatomic, readonly) double masternodeListProgress;
+@property (nonatomic, readonly) double chainSyncProgress;
+@property (nonatomic, readonly) double terminalHeaderSyncProgress;
+@property (nonatomic, readonly) double combinedSyncProgress;
+@property (nonatomic, readonly) DSSyncStateKind kind;
+
+// MARK: Constructor
+
+- (instancetype)initWithSyncPhase:(DSChainSyncPhase)phase;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/DashSync/shared/Models/Notifications/DSSyncState.m b/DashSync/shared/Models/Notifications/DSSyncState.m
new file mode 100644
index 000000000..8b5481480
--- /dev/null
+++ b/DashSync/shared/Models/Notifications/DSSyncState.m
@@ -0,0 +1,191 @@
+//
+// Created by Vladimir Pirogov
+// Copyright © 2024 Dash Core Group. All rights reserved.
+//
+// Licensed under the MIT License (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "DSOptionsManager.h"
+#import "DSSyncState.h"
+
+@implementation DSMasternodeListSyncState
+- (id)copyWithZone:(NSZone *)zone {
+ DSMasternodeListSyncState *copy = [[[self class] alloc] init];
+ copy.retrievalQueueCount = self.retrievalQueueCount;
+ copy.retrievalQueueMaxAmount = self.retrievalQueueMaxAmount;
+ copy.storedCount = self.storedCount;
+ copy.lastBlockHeight = self.lastBlockHeight;
+ return copy;
+}
+- (NSString *)description {
+ return [NSString stringWithFormat:@"%u/%u/%u/%u",
+ self.retrievalQueueCount,
+ self.retrievalQueueMaxAmount,
+ self.storedCount,
+ self.lastBlockHeight];
+}
+@end
+
+@implementation DSSyncState
+
+- (instancetype)initWithSyncPhase:(DSChainSyncPhase)phase {
+ if (!(self = [super init])) return nil;
+ self.syncPhase = phase;
+ self.masternodeListSyncInfo = [[DSMasternodeListSyncState alloc] init];
+ return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ DSSyncState *copy = [[[self class] alloc] init];
+ copy.syncPhase = self.syncPhase;
+ copy.hasDownloadPeer = self.hasDownloadPeer;
+ copy.peerManagerConnected = self.peerManagerConnected;
+ copy.estimatedBlockHeight = self.estimatedBlockHeight;
+ copy.chainSyncStartHeight = self.chainSyncStartHeight;
+ copy.lastSyncBlockHeight = self.lastSyncBlockHeight;
+ copy.terminalSyncStartHeight = self.terminalSyncStartHeight;
+ copy.lastTerminalBlockHeight = self.lastTerminalBlockHeight;
+ copy.masternodeListSyncInfo = [self.masternodeListSyncInfo copy];
+ return copy;
+}
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"SyncState: { phase: %u, peer: %u, connected: %u, estimated: %u, chain: [%u/%u/%f] headers: [%u/%u/%f], mn: [%@/%u/%f] == %f",
+ self.syncPhase,
+ self.hasDownloadPeer,
+ self.peerManagerConnected,
+ self.estimatedBlockHeight,
+ self.chainSyncStartHeight,
+ self.lastSyncBlockHeight,
+ self.chainSyncProgress,
+ self.terminalSyncStartHeight,
+ self.lastTerminalBlockHeight,
+ self.terminalHeaderSyncProgress,
+ self.masternodeListSyncInfo,
+ self.masternodeListsToSync,
+ self.masternodeListProgress,
+ self.combinedSyncProgress
+ ];
+}
+
+- (double)masternodeListProgress {
+ uint32_t amountLeft = self.masternodeListSyncInfo.retrievalQueueCount;
+ uint32_t maxAmount = self.masternodeListSyncInfo.retrievalQueueMaxAmount;
+ uint32_t lastBlockHeight = self.masternodeListSyncInfo.lastBlockHeight;
+ uint32_t estimatedBlockHeight = self.estimatedBlockHeight;
+ return amountLeft ? MAX(MIN((maxAmount - amountLeft) / maxAmount, 1), 0) : lastBlockHeight != UINT32_MAX && estimatedBlockHeight != 0 && lastBlockHeight + 16 >= estimatedBlockHeight;
+}
+
+- (double)chainSyncProgress {
+ uint32_t chainSyncStartHeight = self.chainSyncStartHeight;
+ uint32_t lastSyncBlockHeight = self.lastSyncBlockHeight;
+ uint32_t estimatedBlockHeight = self.estimatedBlockHeight;
+ if (!self.hasDownloadPeer && chainSyncStartHeight == 0)
+ return 0.0;
+ else if (lastSyncBlockHeight >= estimatedBlockHeight)
+ return 1.0;
+ else if (estimatedBlockHeight == 0)
+ return 0.0;
+ else if (chainSyncStartHeight > lastSyncBlockHeight)
+ return MIN(1.0, MAX(0.0, 0.1 + 0.9 * lastSyncBlockHeight / estimatedBlockHeight));
+ double deltaSyncHeight = estimatedBlockHeight - chainSyncStartHeight;
+ return deltaSyncHeight == 0 ? 0.0 : MIN(1.0, MAX(0.0, 0.1 + 0.9 * (lastSyncBlockHeight - chainSyncStartHeight) / deltaSyncHeight));
+}
+
+- (double)terminalHeaderSyncProgress {
+ uint32_t terminalSyncStartHeight = self.terminalSyncStartHeight;
+ uint32_t lastTerminalBlockHeight = self.lastTerminalBlockHeight;
+ uint32_t estimatedBlockHeight = self.estimatedBlockHeight;
+ if (!self.hasDownloadPeer && terminalSyncStartHeight == 0)
+ return 0.0;
+ else if (lastTerminalBlockHeight >= estimatedBlockHeight)
+ return 1.0;
+ else
+ return MIN(1.0, MAX(0.0, 0.1 + 0.9 * (terminalSyncStartHeight > lastTerminalBlockHeight ? lastTerminalBlockHeight / estimatedBlockHeight : (lastTerminalBlockHeight - terminalSyncStartHeight) / (estimatedBlockHeight - terminalSyncStartHeight))));
+}
+
+- (uint32_t)masternodeListsToSync {
+ uint32_t estimatedBlockHeight = self.estimatedBlockHeight;
+ uint32_t amountLeft = self.masternodeListSyncInfo.retrievalQueueCount;
+ uint32_t lastMasternodeListHeight = self.masternodeListSyncInfo.lastBlockHeight;
+ uint32_t maxAmount = self.masternodeListSyncInfo.retrievalQueueMaxAmount;
+ uint32_t storedCount = self.masternodeListSyncInfo.storedCount;
+ uint32_t masternodeListsToSync;
+ if (!([[DSOptionsManager sharedInstance] syncType] & DSSyncType_MasternodeList))
+ masternodeListsToSync = 0;
+ else if (!maxAmount || storedCount <= 1) // 1 because there might be a default
+ masternodeListsToSync = (lastMasternodeListHeight == UINT32_MAX || estimatedBlockHeight < lastMasternodeListHeight)
+ ? 32
+ : MIN(32, (uint32_t)ceil((estimatedBlockHeight - lastMasternodeListHeight) / 24.0f));
+ else
+ masternodeListsToSync = amountLeft;
+
+ return masternodeListsToSync;
+}
+/**
+ * A unit of weight is the time it would take to sync 1000 blocks;
+ * terminal headers are 4 times faster the blocks
+ * the first masternode list is worth 20000 blocks
+ * each masternode list after that is worth 2000 blocks
+ */
+- (double)combinedSyncProgress {
+ uint32_t estimatedBlockHeight = self.estimatedBlockHeight;
+ uint32_t lastTerminalBlockHeight = self.lastTerminalBlockHeight;
+ uint32_t lastSyncBlockHeight = self.lastSyncBlockHeight;
+ double chainWeight = lastSyncBlockHeight >= estimatedBlockHeight ? 0 : estimatedBlockHeight - lastSyncBlockHeight;
+ double terminalWeight = lastTerminalBlockHeight >= estimatedBlockHeight ? 0 : (estimatedBlockHeight - lastTerminalBlockHeight) / 4;
+ uint32_t listsToSync = [self masternodeListsToSync];
+ double masternodeWeight = listsToSync ? (20000 + 2000 * (listsToSync - 1)) : 0;
+ double totalWeight = chainWeight + terminalWeight + masternodeWeight;
+ if (totalWeight == 0) {
+ return self.peerManagerConnected && self.hasDownloadPeer ? 1 : 0;
+ } else {
+ double terminalProgress = self.terminalHeaderSyncProgress * (terminalWeight / totalWeight);
+ double chainProgress = self.chainSyncProgress * (chainWeight / totalWeight);
+ double masternodesProgress = self.masternodeListProgress * (masternodeWeight / totalWeight);
+ double progress = terminalProgress + masternodesProgress + chainProgress;
+ if (progress < 0.99995) {
+ return progress;
+ } else {
+ return 1;
+ }
+ }
+}
+
+- (DSSyncStateKind)kind {
+ if ([self atTheEndOfSyncBlocksAndSyncingMasternodeList] || [self atTheEndOfInitialTerminalBlocksAndSyncingMasternodeList]) {
+ return DSSyncStateKind_Masternodes;
+ } else if (self.syncPhase == DSChainSyncPhase_InitialTerminalBlocks) {
+ return DSSyncStateKind_Headers;
+ } else {
+ return DSSyncStateKind_Chain;
+ }
+
+}
+
+- (BOOL)atTheEndOfSyncBlocksAndSyncingMasternodeList {
+ // We give a 6 block window, just in case a new block comes in
+ return self.lastSyncBlockHeight + 6 >= self.estimatedBlockHeight
+ && self.masternodeListSyncInfo.retrievalQueueCount > 0
+ && self.syncPhase == DSChainSyncPhase_Synced;
+}
+
+- (BOOL)atTheEndOfInitialTerminalBlocksAndSyncingMasternodeList {
+ // We give a 6 block window, just in case a new block comes in
+ return self.lastTerminalBlockHeight + 6 >= self.estimatedBlockHeight
+ && self.masternodeListSyncInfo.retrievalQueueCount > 0
+ && self.syncPhase == DSChainSyncPhase_InitialTerminalBlocks;
+}
+
+@end
+
diff --git a/DashSync/shared/Models/Wallet/DSWalletConstants.m b/DashSync/shared/Models/Wallet/DSWalletConstants.m
index 96e679eed..8392923de 100644
--- a/DashSync/shared/Models/Wallet/DSWalletConstants.m
+++ b/DashSync/shared/Models/Wallet/DSWalletConstants.m
@@ -11,14 +11,13 @@
NSString *const DSChainManagerSyncWillStartNotification = @"DSChainManagerSyncWillStartNotification";
NSString *const DSChainManagerChainSyncDidStartNotification = @"DSChainManagerSyncDidStartNotification";
-NSString *const DSChainManagerSyncParametersUpdatedNotification = @"DSChainManagerSyncParametersUpdatedNotification";
NSString *const DSChainManagerSyncFinishedNotification = @"DSChainManagerSyncFinishedNotification";
NSString *const DSChainManagerSyncFailedNotification = @"DSChainManagerSyncFailedNotification";
+NSString *const DSChainManagerSyncStateDidChangeNotification = @"DSChainManagerSyncStateDidChangeNotification";
NSString *const DSTransactionManagerTransactionStatusDidChangeNotification = @"DSTransactionManagerTransactionStatusDidChangeNotification";
NSString *const DSTransactionManagerTransactionReceivedNotification = @"DSTransactionManagerTransactionReceivedNotification";
-NSString *const DSChainNewChainTipBlockNotification = @"DSChainNewChainTipBlockNotification";
NSString *const DSPeerManagerPeersDidChangeNotification = @"DSPeerManagerPeersDidChangeNotification";
NSString *const DSPeerManagerConnectedPeersDidChangeNotification = @"DSPeerManagerConnectedPeersDidChangeNotification";
NSString *const DSPeerManagerDownloadPeerDidChangeNotification = @"DSPeerManagerDownloadPeerDidChangeNotification";
@@ -27,8 +26,6 @@
NSString *const DSChainStandaloneDerivationPathsDidChangeNotification = @"DSChainStandaloneDerivationPathsDidChangeNotification";
NSString *const DSChainStandaloneAddressesDidChangeNotification = @"DSChainStandaloneAddressesDidChangeNotification";
-NSString *const DSChainChainSyncBlocksDidChangeNotification = @"DSChainChainSyncBlocksDidChangeNotification";
-NSString *const DSChainTerminalBlocksDidChangeNotification = @"DSChainTerminalBlocksDidChangeNotification";
NSString *const DSChainInitialHeadersDidFinishSyncingNotification = @"DSChainInitialHeadersDidFinishSyncingNotification";
NSString *const DSChainBlocksDidFinishSyncingNotification = @"DSChainBlocksDidFinishSyncingNotification";
NSString *const DSChainBlockWasLockedNotification = @"DSChainBlockWasLockedNotification";
@@ -66,6 +63,7 @@
NSString *const DSChainManagerNotificationWalletKey = @"DSChainManagerNotificationWalletKey";
NSString *const DSChainManagerNotificationAccountKey = @"DSChainManagerNotificationAccountKey";
+NSString *const DSChainManagerNotificationSyncStateKey = @"DSChainManagerNotificationSyncStateKey";
NSString *const DSPeerManagerNotificationPeerKey = @"DSPeerManagerNotificationPeerKey";
NSString *const DSTransactionManagerNotificationTransactionKey = @"DSTransactionManagerNotificationTransactionKey";
diff --git a/Example/DashSync.xcodeproj/project.pbxproj b/Example/DashSync.xcodeproj/project.pbxproj
index fb909d947..c38885558 100644
--- a/Example/DashSync.xcodeproj/project.pbxproj
+++ b/Example/DashSync.xcodeproj/project.pbxproj
@@ -2401,6 +2401,7 @@
DevelopmentTeam = 44RJ69WHFF;
};
6003F5AD195388D20070C39A = {
+ DevelopmentTeam = 44RJ69WHFF;
TestTargetID = 6003F589195388D20070C39A;
};
FB9C9EF925B80DB40039880E = {
@@ -2412,10 +2413,9 @@
};
buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "DashSync" */;
compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
+ developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
- English,
en,
Base,
fr,
@@ -3229,6 +3229,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\"",
+ "-ld_classic",
+ );
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
@@ -3247,6 +3289,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\"",
+ "-ld_classic",
+ );
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
@@ -3258,6 +3342,7 @@
baseConfigurationReference = C90A9AD2C32828341F0E6915 /* Pods-DashSync_Tests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
+ DEVELOPMENT_TEAM = 44RJ69WHFF;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
@@ -3279,6 +3364,7 @@
baseConfigurationReference = DFA886AB709D97089A70E82A /* Pods-DashSync_Tests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
+ DEVELOPMENT_TEAM = 44RJ69WHFF;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
diff --git a/Example/DashSync/DSMiningViewController.m b/Example/DashSync/DSMiningViewController.m
index 32eeaeaf1..cd8723ce2 100644
--- a/Example/DashSync/DSMiningViewController.m
+++ b/Example/DashSync/DSMiningViewController.m
@@ -19,6 +19,7 @@
#import "BigIntTypes.h"
#import "DSChain+Protected.h"
#import "DSChainManager.h"
+#import "DSChainManager+Mining.h"
#import "DSFullBlock.h"
#import "NSData+DSHash.h"
#import "NSData+Dash.h"
diff --git a/Example/DashSync/DSSyncViewController.m b/Example/DashSync/DSSyncViewController.m
index 4377bb1ac..a560aa3c9 100644
--- a/Example/DashSync/DSSyncViewController.m
+++ b/Example/DashSync/DSSyncViewController.m
@@ -64,7 +64,7 @@ @interface DSSyncViewController ()
@property (strong, nonatomic) IBOutlet UILabel *masternodeListsCountLabel;
@property (strong, nonatomic) IBOutlet UILabel *earliestMasternodeListLabel;
@property (strong, nonatomic) IBOutlet UILabel *lastMasternodeListLabel;
-@property (strong, nonatomic) id filterChangedObserver, syncFinishedObserver, syncFailedObserver, balanceObserver, blocksObserver, blocksResetObserver, headersResetObserver, sporkObserver, masternodeObserver, masternodeCountObserver, chainWalletObserver, chainStandaloneDerivationPathObserver, chainSingleAddressObserver, governanceObjectCountObserver, governanceObjectReceivedCountObserver, governanceVoteCountObserver, governanceVoteReceivedCountObserver, connectedPeerConnectionObserver, peerConnectionObserver, blockchainIdentitiesObserver, blockchainInvitationsObserver, quorumObserver;
+@property (strong, nonatomic) id filterChangedObserver, syncFinishedObserver, syncFailedObserver, balanceObserver, syncStateObserver, sporkObserver, masternodeObserver, masternodeCountObserver, chainWalletObserver, chainStandaloneDerivationPathObserver, chainSingleAddressObserver, governanceObjectCountObserver, governanceObjectReceivedCountObserver, governanceVoteCountObserver, governanceVoteReceivedCountObserver, connectedPeerConnectionObserver, peerConnectionObserver, blockchainIdentitiesObserver, blockchainInvitationsObserver, quorumObserver;
@property (strong, nonatomic) DSPasteboardAddressExtractor *pasteboardExtractor;
- (IBAction)startSync:(id)sender;
@@ -151,34 +151,18 @@ - (void)viewDidLoad {
}];
- self.blocksObserver =
- [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification
- object:nil
- queue:nil
- usingBlock:^(NSNotification *note) {
- if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) {
- //DSLogPrivate(@"update blockheight");
- [self updateBlockHeight];
- [self updateHeaderHeight];
- }
- }];
- self.blocksResetObserver =
- [[NSNotificationCenter defaultCenter] addObserverForName:DSChainChainSyncBlocksDidChangeNotification
+ self.syncStateObserver =
+ [[NSNotificationCenter defaultCenter] addObserverForName:DSChainManagerSyncStateDidChangeNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
- [self updateBlockHeight];
- [self updateBalance];
- }];
-
- self.headersResetObserver =
- [[NSNotificationCenter defaultCenter] addObserverForName:DSChainTerminalBlocksDidChangeNotification
- object:nil
- queue:nil
- usingBlock:^(NSNotification *note) {
- [self updateHeaderHeight];
- }];
+ if ([note.userInfo[DSChainManagerNotificationChainKey] isEqual:[self chain]]) {
+ [self updateBlockHeight];
+ [self updateHeaderHeight];
+ [self updateBalance];
+ }
+ }];
self.balanceObserver =
[[NSNotificationCenter defaultCenter] addObserverForName:DSWalletBalanceDidChangeNotification
diff --git a/Example/DashSync/DSTransactionFloodingViewController.m b/Example/DashSync/DSTransactionFloodingViewController.m
index 8c63330d3..dc705b2ab 100644
--- a/Example/DashSync/DSTransactionFloodingViewController.m
+++ b/Example/DashSync/DSTransactionFloodingViewController.m
@@ -35,7 +35,7 @@ @interface DSTransactionFloodingViewController ()
@property (nonatomic, strong) IBOutlet UIBarButtonItem *startFloodingButton;
-@property (strong, nonatomic) id blocksObserver, txStatusObserver;
+@property (strong, nonatomic) id syncStateObserver, txStatusObserver;
@property (strong, nonatomic) NSMutableDictionary *transactionSuccessDictionary;
@@ -83,8 +83,8 @@ - (void)viewDidLoad {
}];
}
- self.blocksObserver =
- [[NSNotificationCenter defaultCenter] addObserverForName:DSChainNewChainTipBlockNotification
+ self.syncStateObserver =
+ [[NSNotificationCenter defaultCenter] addObserverForName:DSChainManagerSyncStateDidChangeNotification
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
@@ -100,7 +100,7 @@ - (void)viewDidLoad {
}
- (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self.blocksObserver];
+ [[NSNotificationCenter defaultCenter] removeObserver:self.syncStateObserver];
}
- (void)updateISCounts {
diff --git a/Example/Podfile.lock b/Example/Podfile.lock
index ef3d798df..b0fb2e24d 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.26.1)
+ - Protobuf (3.27.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: a53f5173a603075b3522a5c50be63a67a5f3353a
+ Protobuf: fb2c13674723f76ff6eede14f78847a776455fa2
SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764
tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590
TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80
diff --git a/Example/Tests/DSDeterministicMasternodeListTests.m b/Example/Tests/DSDeterministicMasternodeListTests.m
index f8e9b31e6..b874364f4 100644
--- a/Example/Tests/DSDeterministicMasternodeListTests.m
+++ b/Example/Tests/DSDeterministicMasternodeListTests.m
@@ -2875,7 +2875,7 @@ - (void)testMNLSavingAndRetrievingInIncorrectOrderFromDisk {
XCTAssert(result1092940.validQuorums, @"validQuorums not valid at height %u", [chain heightForBlockHash:blockHash1092940]);
DSQuorumEntry *quorum1092912 = [result1092940.addedQuorums firstObject];
// 1092912 and 1092916 are the same, 1092916 is older though and is original 1092912 is based off a reloaded 109
- UInt256 llmqHash1092912 = [chain.chainManager.masternodeManager buildLLMQHashFor:quorum1092912];
+ UInt256 llmqHash1092912 = [DSKeyManager NSDataFrom:quorum_build_llmq_hash(quorum1092912.llmqType, quorum1092912.quorumHash.u8)].UInt256;
NSArray *masternodeScores1092912 = [masternodeList1092912 scoresForQuorumModifier:llmqHash1092912 atBlockHeight:1092912];
NSArray *masternodeScores1092916 = [masternodeList1092916 scoresForQuorumModifier:llmqHash1092912 atBlockHeight:1092912];
// BOOL a = [quorum1092912 validateWithMasternodeList:masternodeList1092912];
diff --git a/Example/Tests/DSMiningTests.m b/Example/Tests/DSMiningTests.m
index c2b6d559e..95799ae6e 100644
--- a/Example/Tests/DSMiningTests.m
+++ b/Example/Tests/DSMiningTests.m
@@ -21,6 +21,7 @@
#import "DSAccount.h"
#import "DSChain+Protected.h"
#import "DSChainManager.h"
+#import "DSChainManager+Mining.h"
#import "DSFullBlock.h"
#import "DSWallet.h"
#import "NSData+DSHash.h"