diff --git a/DashSync.podspec b/DashSync.podspec index b7351785b..3c258b0d5 100644 --- a/DashSync.podspec +++ b/DashSync.podspec @@ -34,7 +34,7 @@ Pod::Spec.new do |s| s.ios.framework = 'UIKit' s.macos.framework = 'Cocoa' s.compiler_flags = '-Wno-comma' - s.dependency 'DashSharedCore', '0.4.10' + s.dependency 'DashSharedCore', '0.4.11' s.dependency 'CocoaLumberjack', '3.7.2' s.ios.dependency 'DWAlertController', '0.2.1' s.dependency 'DSDynamicOptions', '0.1.2' diff --git a/DashSync/shared/DashSync.m b/DashSync/shared/DashSync.m index e3f8dfa89..e97d7c9a0 100644 --- a/DashSync/shared/DashSync.m +++ b/DashSync/shared/DashSync.m @@ -121,7 +121,7 @@ - (void)startSyncForChain:(DSChain *)chain { - (void)stopSyncAllChains { NSArray *chains = [[DSChainsManager sharedInstance] chains]; for (DSChain *chain in chains) { - [[[DSChainsManager sharedInstance] chainManagerForChain:chain].peerManager disconnect]; + [[[DSChainsManager sharedInstance] chainManagerForChain:chain].peerManager disconnect: DSDisconnectReason_ChainWipe]; } } @@ -136,7 +136,7 @@ - (void)wipePeerDataForChain:(DSChain *)chain inContext:(NSManagedObjectContext [self stopSyncForChain:chain]; [[[DSChainsManager sharedInstance] chainManagerForChain:chain].peerManager removeTrustedPeerHost]; - [[[DSChainsManager sharedInstance] chainManagerForChain:chain].peerManager clearPeers]; + [[[DSChainsManager sharedInstance] chainManagerForChain:chain].peerManager clearPeers:DSDisconnectReason_ChainWipe]; [context performBlockAndWait:^{ DSChainEntity *chainEntity = [chain chainEntityInContext:context]; [DSPeerEntity deletePeersForChainEntity:chainEntity]; diff --git a/DashSync/shared/Models/Chain/DSChain.m b/DashSync/shared/Models/Chain/DSChain.m index e9094b943..cd560f147 100644 --- a/DashSync/shared/Models/Chain/DSChain.m +++ b/DashSync/shared/Models/Chain/DSChain.m @@ -178,6 +178,8 @@ - (instancetype)init { if (!(self = [super init])) return nil; NSAssert([NSThread isMainThread], @"Chains should only be created on main thread (for chain entity optimizations)"); self.mOrphans = [NSMutableDictionary dictionary]; + self.mSyncBlocks = [NSMutableDictionary dictionary]; + self.mTerminalBlocks = [NSMutableDictionary dictionary]; self.mWallets = [NSMutableArray array]; self.estimatedBlockHeights = [NSMutableDictionary dictionary]; @@ -209,6 +211,8 @@ - (instancetype)initWithType:(ChainType)type checkpoints:(NSArray *)checkpoints self.headersMaxAmount = chain_headers_max_amount(type); self.checkpoints = checkpoints; self.genesisHash = self.checkpoints[0].blockHash; + _checkpointsByHashDictionary = [NSMutableDictionary dictionary]; + _checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; dispatch_sync(self.networkingQueue, ^{ self.chainManagedObjectContext = [NSManagedObjectContext chainContext]; }); @@ -763,55 +767,59 @@ - (void)setIsRotatedQuorumsPresented:(BOOL)isRotatedQuorumsPresented { } - (uint32_t)minProtocolVersion { - if (_cachedMinProtocolVersion) return _cachedMinProtocolVersion; - switch (self.chainType.tag) { - case ChainType_MainNet: { - NSError *error = nil; - uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); - if (!error && minProtocolVersion) - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET); - else - _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_MAINNET; - break; - } - case ChainType_TestNet: { - NSError *error = nil; - uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); - if (!error && minProtocolVersion) - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET); - else - _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_TESTNET; - break; - } - case ChainType_DevNet: { - NSError *error = nil; - uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); - if (!error && minProtocolVersion) - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET); - else - _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_DEVNET; - break; + @synchronized(self) { + if (_cachedMinProtocolVersion) return _cachedMinProtocolVersion; + switch (self.chainType.tag) { + case ChainType_MainNet: { + NSError *error = nil; + uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); + if (!error && minProtocolVersion) + _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET); + else + _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_MAINNET; + break; + } + case ChainType_TestNet: { + NSError *error = nil; + uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); + if (!error && minProtocolVersion) + _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET); + else + _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_TESTNET; + break; + } + case ChainType_DevNet: { + NSError *error = nil; + uint32_t minProtocolVersion = (uint32_t)getKeychainInt([NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], &error); + if (!error && minProtocolVersion) + _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET); + else + _cachedMinProtocolVersion = DEFAULT_MIN_PROTOCOL_VERSION_DEVNET; + break; + } } + return _cachedMinProtocolVersion; } - return _cachedMinProtocolVersion; } - (void)setMinProtocolVersion:(uint32_t)minProtocolVersion { - if (minProtocolVersion < MIN_VALID_MIN_PROTOCOL_VERSION || minProtocolVersion > MAX_VALID_MIN_PROTOCOL_VERSION) return; - switch (self.chainType.tag) { - case ChainType_MainNet: - setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET), [NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET); - break; - case ChainType_TestNet: - setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET), [NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET); - break; - case ChainType_DevNet: { - setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); - _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET); - break; + @synchronized(self) { + if (minProtocolVersion < MIN_VALID_MIN_PROTOCOL_VERSION || minProtocolVersion > MAX_VALID_MIN_PROTOCOL_VERSION) return; + switch (self.chainType.tag) { + case ChainType_MainNet: + setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET), [NSString stringWithFormat:@"MAINNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); + _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_MAINNET); + break; + case ChainType_TestNet: + setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET), [NSString stringWithFormat:@"TESTNET_%@", DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); + _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_TESTNET); + break; + case ChainType_DevNet: { + setKeychainInt(MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET), [NSString stringWithFormat:@"%@%@", [DSKeyManager devnetIdentifierFor:self.chainType], DEFAULT_MIN_PROTOCOL_VERSION_LOCATION], NO); + _cachedMinProtocolVersion = MAX(minProtocolVersion, DEFAULT_MIN_PROTOCOL_VERSION_DEVNET); + break; + } } } } @@ -1352,17 +1360,6 @@ - (DSCheckpoint *)checkpointForBlockHeight:(uint32_t)blockHeight { return [self.checkpointsByHeightDictionary objectForKey:@(blockHeight)]; } - -- (NSMutableDictionary *)checkpointsByHashDictionary { - if (!_checkpointsByHashDictionary) [self mSyncBlocks]; - return _checkpointsByHashDictionary; -} - -- (NSMutableDictionary *)checkpointsByHeightDictionary { - if (!_checkpointsByHeightDictionary) [self mSyncBlocks]; - return _checkpointsByHeightDictionary; -} - - (void)useCheckpointBeforeOrOnHeightForTerminalBlocksSync:(uint32_t)blockHeight { DSCheckpoint *checkpoint = [self lastCheckpointOnOrBeforeHeight:blockHeight]; self.terminalHeadersOverrideUseCheckpoint = checkpoint; @@ -1566,32 +1563,28 @@ - (DSBlock *)lastSyncBlockWithUseCheckpoints:(BOOL)useCheckpoints { } - (NSMutableDictionary *)mSyncBlocks { - if (_mSyncBlocks.count > 0) { - if (!_checkpointsByHashDictionary) _checkpointsByHashDictionary = [NSMutableDictionary dictionary]; - if (!_checkpointsByHeightDictionary) _checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; - return _mSyncBlocks; - } - - [self.chainManagedObjectContext performBlockAndWait:^{ - if (self->_mSyncBlocks.count > 0) return; - self->_mSyncBlocks = [NSMutableDictionary dictionary]; - - if (uint256_is_not_zero(self.lastPersistedChainSyncBlockHash)) { - self->_mSyncBlocks[uint256_obj(self.lastPersistedChainSyncBlockHash)] = [[DSMerkleBlock alloc] initWithVersion:2 blockHash:self.lastPersistedChainSyncBlockHash prevBlock:UINT256_ZERO timestamp:self.lastPersistedChainSyncBlockTimestamp height:self.lastPersistedChainSyncBlockHeight chainWork:self.lastPersistedChainSyncBlockChainWork onChain:self]; - } - - self.checkpointsByHashDictionary = [NSMutableDictionary dictionary]; - self.checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; - for (DSCheckpoint *checkpoint in self.checkpoints) { // add checkpoints to the block collection - UInt256 checkpointHash = checkpoint.blockHash; - - self->_mSyncBlocks[uint256_obj(checkpointHash)] = [[DSBlock alloc] initWithCheckpoint:checkpoint onChain:self]; - self.checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; - self.checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; + @synchronized (_mSyncBlocks) { + if (_mSyncBlocks.count > 0) { + return _mSyncBlocks; } - }]; - return _mSyncBlocks; + [self.chainManagedObjectContext performBlockAndWait:^{ + if (self->_mSyncBlocks.count > 0) return; + if (uint256_is_not_zero(self.lastPersistedChainSyncBlockHash)) { + self->_mSyncBlocks[uint256_obj(self.lastPersistedChainSyncBlockHash)] = [[DSMerkleBlock alloc] initWithVersion:2 blockHash:self.lastPersistedChainSyncBlockHash prevBlock:UINT256_ZERO timestamp:self.lastPersistedChainSyncBlockTimestamp height:self.lastPersistedChainSyncBlockHeight chainWork:self.lastPersistedChainSyncBlockChainWork onChain:self]; + } + + for (DSCheckpoint *checkpoint in self.checkpoints) { // add checkpoints to the block collection + UInt256 checkpointHash = checkpoint.blockHash; + + self->_mSyncBlocks[uint256_obj(checkpointHash)] = [[DSBlock alloc] initWithCheckpoint:checkpoint onChain:self]; + self->_checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; + self->_checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; + } + }]; + + return _mSyncBlocks; + } } - (NSArray *)chainSyncBlockLocatorArray { @@ -1643,7 +1636,8 @@ - (NSMutableDictionary *)mSyncBlocks { - (DSBlock *_Nullable)blockForBlockHash:(UInt256)blockHash { - DSBlock *b = self.mSyncBlocks[uint256_obj(blockHash)]; + DSBlock *b; + b = self.mSyncBlocks[uint256_obj(blockHash)]; if (b) return b; b = self.mTerminalBlocks[uint256_obj(blockHash)]; if (b) return b; @@ -1761,15 +1755,9 @@ - (BOOL)addMinedFullBlock:(DSFullBlock *)block { if (!uint256_eq(self.lastSyncBlock.blockHash, self.mSyncBlocks[prevBlock].blockHash)) return NO; if (!uint256_eq(self.lastTerminalBlock.blockHash, self.mTerminalBlocks[prevBlock].blockHash)) return NO; - - @synchronized(self.mSyncBlocks) { - self.mSyncBlocks[blockHash] = block; - } + self.mSyncBlocks[blockHash] = block; self.lastSyncBlock = block; - - @synchronized(self.mTerminalBlocks) { - self.mTerminalBlocks[blockHash] = block; - } + self.mTerminalBlocks[blockHash] = block; self.lastTerminalBlock = block; uint32_t txTime = block.timestamp / 2 + self.mTerminalBlocks[prevBlock].timestamp / 2; @@ -1777,7 +1765,9 @@ - (BOOL)addMinedFullBlock:(DSFullBlock *)block { [self setBlockHeight:block.height andTimestamp:txTime forTransactionHashes:txHashes]; if (block.height > self.estimatedBlockHeight) { - _bestEstimatedBlockHeight = block.height; + @synchronized (self) { + _bestEstimatedBlockHeight = block.height; + } [self saveBlockLocators]; [self saveTerminalBlocks]; @@ -1854,7 +1844,9 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( BOOL syncDone = NO; - block.height = prev.height + 1; + @synchronized (block) { + block.height = prev.height + 1; + } UInt256 target = setCompactLE(block.target); NSAssert(uint256_is_not_zero(prev.chainWork), @"previous block should have aggregate work set"); block.chainWork = uInt256AddLE(prev.chainWork, uInt256AddOneLE(uInt256DivideLE(uint256_inverse(target), uInt256AddOneLE(target)))); @@ -1862,35 +1854,31 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( uint32_t txTime = block.timestamp / 2 + prev.timestamp / 2; if ((blockPosition & DSBlockPosition_Terminal) && ((block.height % 10000) == 0 || ((block.height == self.estimatedBlockHeight) && (block.height % 100) == 0))) { //free up some memory from time to time - @synchronized(self.mTerminalBlocks) { - //[self saveTerminalBlocks]; - DSBlock *b = block; - - for (uint32_t i = 0; b && i < KEEP_RECENT_TERMINAL_BLOCKS; i++) { - b = self.mTerminalBlocks[b.prevBlockValue]; - } - NSMutableArray *blocksToRemove = [NSMutableArray array]; - while (b) { // free up some memory - [blocksToRemove addObject:b.blockHashValue]; - b = self.mTerminalBlocks[b.prevBlockValue]; - } - [self.mTerminalBlocks removeObjectsForKeys:blocksToRemove]; + //[self saveTerminalBlocks]; + DSBlock *b = block; + + for (uint32_t i = 0; b && i < KEEP_RECENT_TERMINAL_BLOCKS; i++) { + b = self.mTerminalBlocks[b.prevBlockValue]; + } + NSMutableArray *blocksToRemove = [NSMutableArray array]; + while (b) { // free up some memory + [blocksToRemove addObject:b.blockHashValue]; + b = self.mTerminalBlocks[b.prevBlockValue]; } + [self.mTerminalBlocks removeObjectsForKeys:blocksToRemove]; } if ((blockPosition & DSBlockPosition_Sync) && ((block.height % 1000) == 0)) { //free up some memory from time to time - @synchronized(self.mSyncBlocks) { - DSBlock *b = block; - - for (uint32_t i = 0; b && i < KEEP_RECENT_SYNC_BLOCKS; i++) { - b = self.mSyncBlocks[b.prevBlockValue]; - } - NSMutableArray *blocksToRemove = [NSMutableArray array]; - while (b) { // free up some memory - [blocksToRemove addObject:b.blockHashValue]; - b = self.mSyncBlocks[b.prevBlockValue]; - } - [self.mSyncBlocks removeObjectsForKeys:blocksToRemove]; + DSBlock *b = block; + + for (uint32_t i = 0; b && i < KEEP_RECENT_SYNC_BLOCKS; i++) { + b = self.mSyncBlocks[b.prevBlockValue]; + } + NSMutableArray *blocksToRemove = [NSMutableArray array]; + while (b) { // free up some memory + [blocksToRemove addObject:b.blockHashValue]; + b = self.mSyncBlocks[b.prevBlockValue]; } + [self.mSyncBlocks removeObjectsForKeys:blocksToRemove]; } // verify block difficulty if block is past last checkpoint @@ -1899,12 +1887,9 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( DSBlock *equivalentTerminalBlock = nil; if ((blockPosition & DSBlockPosition_Sync) && (self.lastSyncBlockHeight + 1 >= lastCheckpoint.height)) { - @synchronized(self.mTerminalBlocks) { - equivalentTerminalBlock = self.mTerminalBlocks[blockHash]; - } + equivalentTerminalBlock = self.mTerminalBlocks[blockHash]; } - if (!equivalentTerminalBlock && ((blockPosition & DSBlockPosition_Terminal) || [block canCalculateDifficultyWithPreviousBlocks:self.mSyncBlocks])) { //no need to check difficulty if we already have terminal blocks uint32_t foundDifficulty = 0; if ((block.height > self.minimumDifficultyBlocks) && (block.height > (lastCheckpoint.height + DGW_PAST_BLOCKS_MAX)) && @@ -1944,117 +1929,108 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( BOOL onMainChain = FALSE; + uint32_t h = block.height; if ((phase == DSChainSyncPhase_ChainSync || phase == DSChainSyncPhase_Synced) && uint256_eq(block.prevBlock, self.lastSyncBlockHash)) { // new block extends sync chain - if ((block.height % 1000) == 0 || txHashes.count > 0 || block.height > peer.lastBlockHeight) { - DSLog(@"[%@: %@] + sync block at: %d: %@", self.name, peer.host ? peer.host : @"TEST", block.height, uint256_hex(block.blockHash)); - } - @synchronized(self.mSyncBlocks) { - self.mSyncBlocks[blockHash] = block; + if ((block.height % 1000) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) { + DSLog(@"[%@: %@] + sync block at: %d: %@", self.name, peer.host ? peer.host : @"TEST", h, uint256_hex(block.blockHash)); } + self.mSyncBlocks[blockHash] = block; if (equivalentTerminalBlock && equivalentTerminalBlock.chainLocked && !block.chainLocked) { [block setChainLockedWithEquivalentBlock:equivalentTerminalBlock]; } self.lastSyncBlock = block; if (!equivalentTerminalBlock && uint256_eq(block.prevBlock, self.lastTerminalBlock.blockHash)) { - if ((block.height % 1000) == 0 || txHashes.count > 0 || block.height > peer.lastBlockHeight) { - DSLog(@"[%@: %@] + terminal block (caught up) at: %d: %@", self.name, peer.host ? peer.host : @"TEST", block.height, uint256_hex(block.blockHash)); - } - @synchronized(self.mTerminalBlocks) { - self.mTerminalBlocks[blockHash] = block; + if ((h % 1000) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) { + DSLog(@"[%@: %@] + terminal block (caught up) at: %d: %@", self.name, peer.host ? peer.host : @"TEST", h, uint256_hex(block.blockHash)); } + self.mTerminalBlocks[blockHash] = block; self.lastTerminalBlock = block; } - if (peer) { - peer.currentBlockHeight = block.height; //might be download peer instead + @synchronized(peer) { + if (peer) { + peer.currentBlockHeight = h; //might be download peer instead + } } - if (block.height == self.estimatedBlockHeight) syncDone = YES; + if (h == self.estimatedBlockHeight) syncDone = YES; [self setBlockHeight:block.height andTimestamp:txTime forTransactionHashes:txHashes]; onMainChain = TRUE; - if ([self blockHeightHasCheckpoint:block.height] || - ((block.height % 1000 == 0) && (block.height + BLOCK_NO_FORK_DEPTH < self.lastTerminalBlockHeight) && !self.chainManager.masternodeManager.hasMasternodeListCurrentlyBeingSaved)) { + if ([self blockHeightHasCheckpoint:h] || + ((h % 1000 == 0) && (h + BLOCK_NO_FORK_DEPTH < self.lastTerminalBlockHeight) && !self.chainManager.masternodeManager.hasMasternodeListCurrentlyBeingSaved)) { [self saveBlockLocators]; } } else if (uint256_eq(block.prevBlock, self.lastTerminalBlock.blockHash)) { // new block extends terminal chain - if ((block.height % 500) == 0 || txHashes.count > 0 || block.height > peer.lastBlockHeight) { - DSLog(@"[%@: %@] + terminal block at: %d: %@", self.name, peer.host ? peer.host : @"TEST", block.height, uint256_hex(block.blockHash)); - } - @synchronized(self.mTerminalBlocks) { - self.mTerminalBlocks[blockHash] = block; + if ((h % 500) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) { + DSLog(@"[%@: %@] + terminal block at: %d: %@", self.name, peer.host ? peer.host : @"TEST", h, uint256_hex(block.blockHash)); } + self.mTerminalBlocks[blockHash] = block; self.lastTerminalBlock = block; - if (peer) { - peer.currentBlockHeight = block.height; //might be download peer instead + @synchronized(peer) { + if (peer) { + peer.currentBlockHeight = h; //might be download peer instead + } } - if (block.height == self.estimatedBlockHeight) syncDone = YES; + if (h == self.estimatedBlockHeight) syncDone = YES; onMainChain = TRUE; } else if ((phase == DSChainSyncPhase_ChainSync || phase == DSChainSyncPhase_Synced) && self.mSyncBlocks[blockHash] != nil) { // we already have the block (or at least the header) - if ((block.height % 1) == 0 || txHashes.count > 0 || block.height > peer.lastBlockHeight) { - DSLog(@"%@:%d relayed existing sync block at height %d", peer.host, peer.port, block.height); - } - - @synchronized(self.mSyncBlocks) { - self.mSyncBlocks[blockHash] = block; + if ((h % 1) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) { + DSLog(@"%@:%d relayed existing sync block at height %d", peer.host, peer.port, h); } - + self.mSyncBlocks[blockHash] = block; if (equivalentTerminalBlock && equivalentTerminalBlock.chainLocked && !block.chainLocked) { [block setChainLockedWithEquivalentBlock:equivalentTerminalBlock]; } - if (peer) { - peer.currentBlockHeight = block.height; //might be download peer instead + @synchronized(peer) { + if (peer) { + peer.currentBlockHeight = h; //might be download peer instead + } } - + DSBlock *b = self.lastSyncBlock; - while (b && b.height > block.height) b = self.mSyncBlocks[b.prevBlockValue]; // is block in main chain? + while (b && b.height > h) b = self.mSyncBlocks[b.prevBlockValue]; // is block in main chain? if (b != nil && uint256_eq(b.blockHash, block.blockHash)) { // if it's not on a fork, set block heights for its transactions - [self setBlockHeight:block.height andTimestamp:txTime forTransactionHashes:txHashes]; - if (block.height == self.lastSyncBlockHeight) self.lastSyncBlock = block; + [self setBlockHeight:h andTimestamp:txTime forTransactionHashes:txHashes]; + if (h == self.lastSyncBlockHeight) self.lastSyncBlock = block; } } else if (self.mTerminalBlocks[blockHash] != nil && (blockPosition & DSBlockPosition_Terminal)) { // we already have the block (or at least the header) - if ((block.height % 1) == 0 || txHashes.count > 0 || block.height > peer.lastBlockHeight) { - DSLog(@"%@:%d relayed existing terminal block at height %d (last sync height %d)", peer.host, peer.port, block.height, self.lastSyncBlockHeight); - } - - @synchronized(self.mTerminalBlocks) { - self.mTerminalBlocks[blockHash] = block; + if ((h % 1) == 0 || txHashes.count > 0 || h > peer.lastBlockHeight) { + DSLog(@"%@:%d relayed existing terminal block at height %d (last sync height %d)", peer.host, peer.port, h, self.lastSyncBlockHeight); } - - if (peer) { - peer.currentBlockHeight = block.height; //might be download peer instead + self.mTerminalBlocks[blockHash] = block; + @synchronized(peer) { + if (peer) { + peer.currentBlockHeight = h; //might be download peer instead + } } - + DSBlock *b = self.lastTerminalBlock; - while (b && b.height > block.height) b = self.mTerminalBlocks[b.prevBlockValue]; // is block in main chain? + while (b && b.height > h) b = self.mTerminalBlocks[b.prevBlockValue]; // is block in main chain? if (b != nil && uint256_eq(b.blockHash, block.blockHash)) { // if it's not on a fork, set block heights for its transactions - [self setBlockHeight:block.height andTimestamp:txTime forTransactionHashes:txHashes]; - if (block.height == self.lastTerminalBlockHeight) self.lastTerminalBlock = block; + [self setBlockHeight:h andTimestamp:txTime forTransactionHashes:txHashes]; + if (h == self.lastTerminalBlockHeight) self.lastTerminalBlock = block; } } else { // new block is on a fork - if (block.height <= [self lastCheckpoint].height) { // fork is older than last checkpoint - DSLog(@"ignoring block on fork older than most recent checkpoint, fork height: %d, blockHash: %@", - block.height, blockHash); + if (h <= [self lastCheckpoint].height) { // fork is older than last checkpoint + DSLog(@"ignoring block on fork older than most recent checkpoint, fork height: %d, blockHash: %@", h, blockHash); return TRUE; } - if (block.height <= self.lastChainLock.height) { - DSLog(@"ignoring block on fork when main chain is chainlocked: %d, blockHash: %@", - block.height, blockHash); + if (h <= self.lastChainLock.height) { + DSLog(@"ignoring block on fork when main chain is chainlocked: %d, blockHash: %@", h, blockHash); return TRUE; } DSLog(@"potential chain fork to height %d blockPosition %d", block.height, blockPosition); if (!(blockPosition & DSBlockPosition_Sync)) { //this is only a reorg of the terminal blocks - @synchronized(self.mTerminalBlocks) { - self.mTerminalBlocks[blockHash] = block; - } + self.mTerminalBlocks[blockHash] = block; if (uint256_supeq(self.lastTerminalBlock.chainWork, block.chainWork)) return TRUE; // if fork is shorter than main chain, ignore it for now DSLog(@"found potential chain fork on height %d", block.height); @@ -2066,34 +2042,31 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( } if (!uint256_eq(b.blockHash, b2.blockHash) && b2.chainLocked) { //intermediate chain locked block - DSLog(@"no reorganizing chain to height %d because of chainlock at height %d", block.height, b2.height); + DSLog(@"no reorganizing chain to height %d because of chainlock at height %d", h, b2.height); return TRUE; } - DSLog(@"reorganizing terminal chain from height %d, new height is %d", b.height, block.height); + DSLog(@"reorganizing terminal chain from height %d, new height is %d", b.height, h); self.lastTerminalBlock = block; - if (peer) { - peer.currentBlockHeight = block.height; //might be download peer instead + @synchronized(peer) { + if (peer) { + peer.currentBlockHeight = h; //might be download peer instead + } } - if (block.height == self.estimatedBlockHeight) syncDone = YES; + if (h == self.estimatedBlockHeight) syncDone = YES; } else { if (phase == DSChainSyncPhase_ChainSync || phase == DSChainSyncPhase_Synced) { - @synchronized(self.mTerminalBlocks) { - self.mTerminalBlocks[blockHash] = block; - } - } - - @synchronized(self.mSyncBlocks) { - self.mSyncBlocks[blockHash] = block; + self.mTerminalBlocks[blockHash] = block; } - + self.mSyncBlocks[blockHash] = block; + if (equivalentTerminalBlock && equivalentTerminalBlock.chainLocked && !block.chainLocked) { [block setChainLockedWithEquivalentBlock:equivalentTerminalBlock]; } if (uint256_supeq(self.lastSyncBlock.chainWork, block.chainWork)) return TRUE; // if fork is shorter than main chain, ignore it for now - DSLog(@"found sync chain fork on height %d", block.height); + DSLog(@"found sync chain fork on height %d", h); if ((phase == DSChainSyncPhase_ChainSync || phase == DSChainSyncPhase_Synced) && !uint256_supeq(self.lastTerminalBlock.chainWork, block.chainWork)) { DSBlock *b = block, *b2 = self.lastTerminalBlock; @@ -2103,12 +2076,14 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( } if (!uint256_eq(b.blockHash, b2.blockHash) && b2.chainLocked) { //intermediate chain locked block - DSLog(@"no reorganizing chain to height %d because of chainlock at height %d", block.height, b2.height); + DSLog(@"no reorganizing chain to height %d because of chainlock at height %d", h, b2.height); } else { - DSLog(@"reorganizing terminal chain from height %d, new height is %d", b.height, block.height); + DSLog(@"reorganizing terminal chain from height %d, new height is %d", b.height, h); self.lastTerminalBlock = block; - if (peer) { - peer.currentBlockHeight = block.height; //might be download peer instead + @synchronized(peer) { + if (peer) { + peer.currentBlockHeight = h; //might be download peer instead + } } } } @@ -2121,11 +2096,11 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( } if (!uint256_eq(b.blockHash, b2.blockHash) && b2.chainLocked) { //intermediate chain locked block - DSLog(@"no reorganizing sync chain to height %d because of chainlock at height %d", block.height, b2.height); + DSLog(@"no reorganizing sync chain to height %d because of chainlock at height %d", h, b2.height); return TRUE; } - DSLog(@"reorganizing sync chain from height %d, new height is %d", b.height, block.height); + DSLog(@"reorganizing sync chain from height %d, new height is %d", b.height, h); NSMutableArray *txHashes = [NSMutableArray array]; // mark transactions after the join point as unconfirmed @@ -2136,9 +2111,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( } } - [self setBlockHeight:TX_UNCONFIRMED - andTimestamp:0 - forTransactionHashes:txHashes]; + [self setBlockHeight:TX_UNCONFIRMED andTimestamp:0 forTransactionHashes:txHashes]; b = block; while (b.height > b2.height) { // set transaction heights for new main chain @@ -2148,7 +2121,7 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( } self.lastSyncBlock = block; - if (block.height == self.estimatedBlockHeight) syncDone = YES; + if (h == self.estimatedBlockHeight) syncDone = YES; } } @@ -2183,7 +2156,9 @@ - (BOOL)addBlock:(DSBlock *)block receivedAsHeader:(BOOL)isHeaderOnly fromPeer:( } if (((blockPosition & DSBlockPosition_Terminal) && block.height > self.estimatedBlockHeight) || ((blockPosition & DSBlockPosition_Sync) && block.height >= self.lastTerminalBlockHeight)) { - _bestEstimatedBlockHeight = block.height; + @synchronized (self) { + _bestEstimatedBlockHeight = block.height; + } if (peer && (blockPosition & DSBlockPosition_Sync) && !savedBlockLocators) { [self saveBlockLocators]; } @@ -2255,63 +2230,63 @@ - (void)notifyBlocksChanged:(DSBlockPosition)blockPosition { // MARK: Terminal Blocks - (NSMutableDictionary *)mTerminalBlocks { - if (_mTerminalBlocks.count > 0) { - if (!_checkpointsByHashDictionary) _checkpointsByHashDictionary = [NSMutableDictionary dictionary]; - if (!_checkpointsByHeightDictionary) _checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; - return _mTerminalBlocks; - } - - [self.chainManagedObjectContext performBlockAndWait:^{ - if (self->_mTerminalBlocks.count > 0) return; - self->_mTerminalBlocks = [NSMutableDictionary dictionary]; - self.checkpointsByHashDictionary = [NSMutableDictionary dictionary]; - self.checkpointsByHeightDictionary = [NSMutableDictionary dictionary]; - for (DSCheckpoint *checkpoint in self.checkpoints) { // add checkpoints to the block collection - UInt256 checkpointHash = checkpoint.blockHash; - - self->_mTerminalBlocks[uint256_obj(checkpointHash)] = [[DSBlock alloc] initWithCheckpoint:checkpoint onChain:self]; - self.checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; - self.checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; - } - for (DSMerkleBlockEntity *e in [DSMerkleBlockEntity lastTerminalBlocks:KEEP_RECENT_TERMINAL_BLOCKS onChainEntity:[self chainEntityInContext:self.chainManagedObjectContext]]) { - @autoreleasepool { - DSMerkleBlock *b = e.merkleBlock; + @synchronized (_mTerminalBlocks) { + if (_mTerminalBlocks.count > 0) { + return _mTerminalBlocks; + } + [self.chainManagedObjectContext performBlockAndWait:^{ + if (self->_mTerminalBlocks.count > 0) return; + for (DSCheckpoint *checkpoint in self.checkpoints) { // add checkpoints to the block collection + UInt256 checkpointHash = checkpoint.blockHash; - if (b) self->_mTerminalBlocks[b.blockHashValue] = b; + self->_mTerminalBlocks[uint256_obj(checkpointHash)] = [[DSBlock alloc] initWithCheckpoint:checkpoint onChain:self]; + self->_checkpointsByHeightDictionary[@(checkpoint.height)] = checkpoint; + self->_checkpointsByHashDictionary[uint256_data(checkpointHash)] = checkpoint; } - }; - }]; - - return _mTerminalBlocks; + for (DSMerkleBlockEntity *e in [DSMerkleBlockEntity lastTerminalBlocks:KEEP_RECENT_TERMINAL_BLOCKS onChainEntity:[self chainEntityInContext:self.chainManagedObjectContext]]) { + @autoreleasepool { + DSMerkleBlock *b = e.merkleBlock; + if (b) self->_mTerminalBlocks[b.blockHashValue] = b; + } + }; + }]; + + return _mTerminalBlocks; + } } - (DSBlock *)lastTerminalBlock { - if (_lastTerminalBlock) return _lastTerminalBlock; - + @synchronized (self) { + if (_lastTerminalBlock) return _lastTerminalBlock; + } [self.chainManagedObjectContext performBlockAndWait:^{ NSArray *lastTerminalBlocks = [DSMerkleBlockEntity lastTerminalBlocks:1 onChainEntity:[self chainEntityInContext:self.chainManagedObjectContext]]; DSMerkleBlock *lastTerminalBlock = [[lastTerminalBlocks firstObject] merkleBlock]; - self->_lastTerminalBlock = lastTerminalBlock; - if (lastTerminalBlock) { - DSLog(@"last terminal block at height %d recovered from db (hash is %@)", lastTerminalBlock.height, [NSData dataWithUInt256:lastTerminalBlock.blockHash].hexString); + @synchronized (self) { + self->_lastTerminalBlock = lastTerminalBlock; + if (lastTerminalBlock) { + DSLog(@"last terminal block at height %d recovered from db (hash is %@)", lastTerminalBlock.height, [NSData dataWithUInt256:lastTerminalBlock.blockHash].hexString); + } } }]; - - if (!_lastTerminalBlock) { - // if we don't have any headers yet, use the latest checkpoint - DSCheckpoint *lastCheckpoint = self.terminalHeadersOverrideUseCheckpoint ? self.terminalHeadersOverrideUseCheckpoint : self.lastCheckpoint; - uint32_t lastSyncBlockHeight = self.lastSyncBlockHeight; - - if (lastCheckpoint.height >= lastSyncBlockHeight) { - [self setLastTerminalBlockFromCheckpoints]; - } else { - _lastTerminalBlock = self.lastSyncBlock; + + @synchronized (self) { + if (!_lastTerminalBlock) { + // if we don't have any headers yet, use the latest checkpoint + DSCheckpoint *lastCheckpoint = self.terminalHeadersOverrideUseCheckpoint ? self.terminalHeadersOverrideUseCheckpoint : self.lastCheckpoint; + uint32_t lastSyncBlockHeight = self.lastSyncBlockHeight; + + if (lastCheckpoint.height >= lastSyncBlockHeight) { + [self setLastTerminalBlockFromCheckpoints]; + } else { + _lastTerminalBlock = self.lastSyncBlock; + } } + + if (_lastTerminalBlock.height > self.estimatedBlockHeight) _bestEstimatedBlockHeight = _lastTerminalBlock.height; + + return _lastTerminalBlock; } - - if (_lastTerminalBlock.height > self.estimatedBlockHeight) _bestEstimatedBlockHeight = _lastTerminalBlock.height; - - return _lastTerminalBlock; } - (NSArray *)terminalBlocksLocatorArray { @@ -2450,9 +2425,7 @@ - (BOOL)addChainLock:(DSChainLock *)chainLock { } } - [self setBlockHeight:TX_UNCONFIRMED - andTimestamp:0 - forTransactionHashes:txHashes]; + [self setBlockHeight:TX_UNCONFIRMED andTimestamp:0 forTransactionHashes:txHashes]; clb = syncBlock; while (clb.height > sbmc.height) { // set transaction heights for new main chain @@ -2505,7 +2478,15 @@ - (NSTimeInterval)lastSyncBlockTimestamp { } - (uint32_t)lastSyncBlockHeight { - return _lastSyncBlock ? _lastSyncBlock.height : (self.lastPersistedChainSyncBlockHeight ? self.lastPersistedChainSyncBlockHeight : self.lastSyncBlock.height); + @synchronized (_lastSyncBlock) { + if (_lastSyncBlock) { + return _lastSyncBlock.height; + } else if (self.lastPersistedChainSyncBlockHeight) { + return self.lastPersistedChainSyncBlockHeight; + } else { + return self.lastSyncBlock.height; + } + } } - (UInt256)lastSyncBlockHash { @@ -2521,8 +2502,7 @@ - (uint32_t)lastTerminalBlockHeight { } - (BOOL)allowInsightBlocksForVerification { - if (self.isMainnet) return NO; - return YES; + return !self.isMainnet; } - (uint32_t)quickHeightForBlockHash:(UInt256)blockhash { @@ -2530,17 +2510,19 @@ - (uint32_t)quickHeightForBlockHash:(UInt256)blockhash { if (checkpoint) { return checkpoint.height; } - - DSBlock *syncBlock = [self.mSyncBlocks objectForKey:uint256_obj(blockhash)]; - if (syncBlock && (syncBlock.height != UINT32_MAX)) { - return syncBlock.height; + @synchronized (_mSyncBlocks) { + DSBlock *syncBlock = [_mSyncBlocks objectForKey:uint256_obj(blockhash)]; + if (syncBlock && (syncBlock.height != UINT32_MAX)) { + return syncBlock.height; + } } - - DSBlock *terminalBlock = [self.mTerminalBlocks objectForKey:uint256_obj(blockhash)]; - if (terminalBlock && (terminalBlock.height != UINT32_MAX)) { - return terminalBlock.height; + @synchronized (_mTerminalBlocks) { + DSBlock *terminalBlock = [_mTerminalBlocks objectForKey:uint256_obj(blockhash)]; + if (terminalBlock && (terminalBlock.height != UINT32_MAX)) { + return terminalBlock.height; + } } - + for (DSCheckpoint *checkpoint in self.checkpoints) { if (uint256_eq(checkpoint.blockHash, blockhash)) { return checkpoint.height; @@ -2555,35 +2537,35 @@ - (uint32_t)heightForBlockHash:(UInt256)blockhash { if (checkpoint) { return checkpoint.height; } - - DSBlock *syncBlock = [self.mSyncBlocks objectForKey:uint256_obj(blockhash)]; - if (syncBlock && (syncBlock.height != UINT32_MAX)) { - return syncBlock.height; + @synchronized (_mSyncBlocks) { + DSBlock *syncBlock = [_mSyncBlocks objectForKey:uint256_obj(blockhash)]; + if (syncBlock && (syncBlock.height != UINT32_MAX)) { + return syncBlock.height; + } } - - DSBlock *terminalBlock = [self.mTerminalBlocks objectForKey:uint256_obj(blockhash)]; - if (terminalBlock && (terminalBlock.height != UINT32_MAX)) { - return terminalBlock.height; + @synchronized (_mTerminalBlocks) { + DSBlock *terminalBlock = [_mTerminalBlocks objectForKey:uint256_obj(blockhash)]; + if (terminalBlock && (terminalBlock.height != UINT32_MAX)) { + return terminalBlock.height; + } } - + DSBlock *b = self.lastTerminalBlock; if (!b) { b = self.lastSyncBlock; } - @synchronized(self.mTerminalBlocks) { - while (b && b.height > 0) { - if (uint256_eq(b.blockHash, blockhash)) { - return b.height; - } - b = self.mTerminalBlocks[b.prevBlockValue]; - if (!b) { - b = self.mSyncBlocks[b.prevBlockValue]; - } + while (b && b.height > 0) { + if (uint256_eq(b.blockHash, blockhash)) { + return b.height; + } + b = self.mTerminalBlocks[b.prevBlockValue]; + if (!b) { + b = self.mSyncBlocks[b.prevBlockValue]; } } - + for (DSCheckpoint *checkpoint in self.checkpoints) { if (uint256_eq(checkpoint.blockHash, blockhash)) { return checkpoint.height; @@ -2673,9 +2655,11 @@ - (void)reloadDerivationPaths { } - (uint32_t)estimatedBlockHeight { - if (_bestEstimatedBlockHeight) return _bestEstimatedBlockHeight; - _bestEstimatedBlockHeight = [self decideFromPeerSoftConsensusEstimatedBlockHeight]; - return _bestEstimatedBlockHeight; + @synchronized (self) { + if (_bestEstimatedBlockHeight) return _bestEstimatedBlockHeight; + _bestEstimatedBlockHeight = [self decideFromPeerSoftConsensusEstimatedBlockHeight]; + return _bestEstimatedBlockHeight; + } } - (uint32_t)decideFromPeerSoftConsensusEstimatedBlockHeight { @@ -2926,8 +2910,12 @@ - (void)wipeBlockchainInfoInContext:(NSManagedObjectContext *)context { [self.viewingAccount wipeBlockchainInfo]; [self.chainManager.identitiesManager clearExternalBlockchainIdentities]; _bestBlockHeight = 0; - _mSyncBlocks = nil; - _mTerminalBlocks = nil; + @synchronized (_mSyncBlocks) { + _mSyncBlocks = [NSMutableDictionary dictionary]; + } + @synchronized (_mTerminalBlocks) { + _mTerminalBlocks = [NSMutableDictionary dictionary]; + } _lastSyncBlock = nil; _lastTerminalBlock = nil; _lastPersistedChainSyncLocators = nil; @@ -2950,7 +2938,9 @@ - (void)wipeBlockchainNonTerminalInfoInContext:(NSManagedObjectContext *)context [self.viewingAccount wipeBlockchainInfo]; [self.chainManager.identitiesManager clearExternalBlockchainIdentities]; _bestBlockHeight = 0; - _mSyncBlocks = nil; + @synchronized (_mSyncBlocks) { + _mSyncBlocks = [NSMutableDictionary dictionary]; + } _lastSyncBlock = nil; _lastPersistedChainSyncLocators = nil; _lastPersistedChainSyncBlockHash = UINT256_ZERO; diff --git a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m index 6865b4e52..b64bacc20 100644 --- a/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m +++ b/DashSync/shared/Models/Entities/DSSimplifiedMasternodeEntryEntity+CoreDataClass.m @@ -37,7 +37,7 @@ - (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEnt - (void)updateAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry *)simplifiedMasternodeEntry atBlockHeight:(uint32_t)blockHeight knownOperatorAddresses:(NSDictionary *)knownOperatorAddresses knownVotingAddresses:(NSDictionary *)knownVotingAddresses localMasternodes:(NSDictionary *)localMasternodes { if (self.updateHeight < blockHeight) { - NSAssert(simplifiedMasternodeEntry.updateHeight == blockHeight, @"the block height should be the same as the entry update height"); + //NSAssert(simplifiedMasternodeEntry.updateHeight == blockHeight, @"the block height should be the same as the entry update height"); self.updateHeight = blockHeight; //we should only update if the data received is the most recent if (!uint128_eq(self.ipv6Address.UInt128, simplifiedMasternodeEntry.address)) { @@ -156,6 +156,7 @@ - (void)setAttributesFromSimplifiedMasternodeEntry:(DSSimplifiedMasternodeEntry if (simplifiedMasternodeEntry.updateHeight != blockHeight) { DSLog(@"• setAttributesFromSimplifiedMasternodeEntry: list.height %u != entry.height %u", blockHeight, simplifiedMasternodeEntry.updateHeight); } + // TODO: make sure we're doing // NSAssert(simplifiedMasternodeEntry.updateHeight == blockHeight, ([NSString stringWithFormat:@"the block height (%i) for %@ should be the same as the entry update height (%i)", blockHeight, uint256_hex(simplifiedMasternodeEntry.providerRegistrationTransactionHash), simplifiedMasternodeEntry.updateHeight])); if (!chainEntity) { self.chain = [simplifiedMasternodeEntry.chain chainEntityInContext:self.managedObjectContext]; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m index da0fede06..3d43643c8 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSChainManager.m @@ -346,10 +346,8 @@ - (double)combinedSyncProgress { DSLog(@"combinedSyncProgress breakdown %f %f %f", self.terminalHeaderSyncProgress, self.masternodeManager.masternodeListAndQuorumsSyncProgress, self.chainSyncProgress); #endif if ((self.terminalHeaderSyncWeight + self.chainSyncWeight + self.masternodeListSyncWeight) == 0) { - if (self.peerManager.connected) { - return 1; - } else { - return 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; @@ -471,11 +469,12 @@ - (void)startSync { object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; }); + DSLog(@"startSync -> peerManager::connect"); [self.peerManager connect]; } - (void)stopSync { - [self.peerManager disconnect]; + [self.peerManager disconnect:DSDisconnectReason_ChainSwitch]; self.syncPhase = DSChainSyncPhase_Offline; } @@ -497,6 +496,7 @@ - (void)disconnectedMasternodeListAndBlocksRescan { object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; }); + DSLog(@"disconnectedMasternodeListAndBlocksRescan -> peerManager::connect"); [self.peerManager connect]; } @@ -510,6 +510,7 @@ - (void)disconnectedMasternodeListRescan { object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; }); + DSLog(@"disconnectedMasternodeListRescan -> peerManager::connect"); [self.peerManager connect]; } @@ -523,6 +524,7 @@ - (void)disconnectedSyncBlocksRescan { object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; }); + DSLog(@"disconnectedSyncBlocksRescan -> peerManager::connect"); [self.peerManager connect]; } @@ -650,7 +652,8 @@ - (void)chainShouldStartSyncingBlockchain:(DSChain *)chain onPeer:(DSPeer *)peer } - (void)chainFinishedSyncingInitialHeaders:(DSChain *)chain fromPeer:(DSPeer *)peer onMainChain:(BOOL)onMainChain { - if (onMainChain && peer && (peer == self.peerManager.downloadPeer)) self.lastChainRelayTime = [NSDate timeIntervalSince1970]; + if (onMainChain && peer && (peer == self.peerManager.downloadPeer)) [self relayedNewItem]; + [self.peerManager chainSyncStopped]; if (([[DSOptionsManager sharedInstance] syncType] & DSSyncType_MasternodeList)) { // make sure we care about masternode lists @@ -659,7 +662,7 @@ - (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.lastChainRelayTime = [NSDate timeIntervalSince1970]; + if (onMainChain && peer && (peer == self.peerManager.downloadPeer)) [self relayedNewItem]; DSLog(@"chain finished syncing"); self.chainSyncStartHeight = 0; self.syncPhase = DSChainSyncPhase_Synced; @@ -678,6 +681,7 @@ - (void)syncBlockchain { if (self.syncPhase == DSChainSyncPhase_InitialTerminalBlocks) { self.syncPhase = DSChainSyncPhase_ChainSync; } + DSLog(@"syncBlockchain -> peerManager::connect"); [self.peerManager connect]; } else if (!self.peerManager.masternodeList && self.masternodeManager.currentMasternodeList) { [self.peerManager useMasternodeList:self.masternodeManager.currentMasternodeList withConnectivityNonce:self.sessionConnectivityNonce]; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m index a5098115f..3398b51c3 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSGovernanceSyncManager.m @@ -525,6 +525,7 @@ - (DSGovernanceVote *)peer:(DSPeer *_Nullable)peer requestedVote:(UInt256)voteHa - (void)peer:(DSPeer *)peer ignoredGovernanceSync:(DSGovernanceRequestState)governanceRequestState { [self.peerManager peerMisbehaving:peer errorMessage:@"Ignored Governance Sync"]; + DSLog(@"ignoredGovernanceSync -> peerManager::connect"); [self.peerManager connect]; } diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m index 5fca43392..bdb984a3d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSMasternodeManager.m @@ -184,13 +184,15 @@ - (uint32_t)estimatedMasternodeListsToSync { } - (double)masternodeListAndQuorumsSyncProgress { - double amountLeft = self.masternodeListRetrievalQueueCount; - double maxAmount = self.masternodeListRetrievalQueueMaxAmount; - if (!amountLeft) { - return self.store.masternodeListsAndQuorumsIsSynced; + @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; } - double progress = MAX(MIN((maxAmount - amountLeft) / maxAmount, 1), 0); - return progress; } - (BOOL)currentMasternodeListIsInLast24Hours { @@ -361,7 +363,9 @@ - (BOOL)requestMasternodeListForBlockHeight:(uint32_t)blockHeight error:(NSError - (BOOL)requestMasternodeListForBlockHash:(UInt256)blockHash { self.store.lastQueriedBlockHash = blockHash; NSData *blockHashData = uint256_data(blockHash); - [self.store.masternodeListQueriesNeedingQuorumsValidated addObject:blockHashData]; + @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { + [self.store.masternodeListQueriesNeedingQuorumsValidated addObject:blockHashData]; + } // this is safe [self getMasternodeListsForBlockHashes:[NSOrderedSet orderedSetWithObject:blockHashData]]; return TRUE; @@ -501,14 +505,21 @@ - (void)processMasternodeListDiffResult:(DSMnDiffProcessingResult *)result forPe DSLog(@"•••• processMasternodeListDiffResult: missingMasternodeLists: %@", [self logListSet:neededMissingMasternodeLists]); UInt256 masternodeListBlockHash = masternodeList.blockHash; NSData *masternodeListBlockHashData = uint256_data(masternodeListBlockHash); - if ([neededMissingMasternodeLists count] && [self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:masternodeListBlockHashData]) { + BOOL hasAwaitingQuorumValidation; + @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { + hasAwaitingQuorumValidation = [self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:masternodeListBlockHashData]; + } + + if ([neededMissingMasternodeLists count] && hasAwaitingQuorumValidation) { [self.masternodeListDiffService removeFromRetrievalQueue:masternodeListBlockHashData]; [self processMissingMasternodeLists:neededMissingMasternodeLists forMasternodeList:masternodeList]; completion(); } else { if (uint256_eq(self.store.lastQueriedBlockHash, masternodeListBlockHash)) { self.masternodeListDiffService.currentMasternodeList = masternodeList; - [self.store.masternodeListQueriesNeedingQuorumsValidated removeObject:masternodeListBlockHashData]; + @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { + [self.store.masternodeListQueriesNeedingQuorumsValidated removeObject:masternodeListBlockHashData]; + } } DSLog(@"••• updateStoreWithMasternodeList: %u: %@ (%@)", masternodeList.height, uint256_hex(masternodeListBlockHash), uint256_reverse_hex(masternodeListBlockHash)); [self updateStoreWithMasternodeList:masternodeList addedMasternodes:result.addedMasternodes modifiedMasternodes:result.modifiedMasternodes addedQuorums:result.addedQuorums completion:^(NSError *error) { @@ -579,33 +590,37 @@ - (void)processQRInfoResult:(DSQRInfoProcessingResult *)result forPeer:(DSPeer * NSData *blockHashDataAtH2C = uint256_data(blockHashAtH2C); NSData *blockHashDataAtH3C = uint256_data(blockHashAtH3C); NSData *blockHashDataAtH4C = uint256_data(blockHashAtH4C); + NSMutableSet *masternodeListQueriesNeedingQuorumsValidated; + @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { + masternodeListQueriesNeedingQuorumsValidated = self.store.masternodeListQueriesNeedingQuorumsValidated; + } if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH4C skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH4C count] || ![self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH4C]) { + } else if (![missingMasternodeListsAtH4C count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH4C]) { DSLog(@"••• updateStoreWithMasternodeList (h-4c): %u: %@ (%@)", masternodeListAtH4C.height, uint256_hex(blockHashAtH4C), uint256_reverse_hex(blockHashAtH4C)); [self updateStoreWithMasternodeList:masternodeListAtH4C addedMasternodes:mnListDiffResultAtH4C.addedMasternodes modifiedMasternodes:mnListDiffResultAtH4C.modifiedMasternodes addedQuorums:mnListDiffResultAtH4C.addedQuorums completion:^(NSError *error) {}]; } if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH3C skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH3C count] || ![self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH3C]) { + } else if (![missingMasternodeListsAtH3C count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH3C]) { DSLog(@"••• updateStoreWithMasternodeList (h-3c): %u: %@ (%@)", masternodeListAtH3C.height, uint256_hex(blockHashAtH3C), uint256_reverse_hex(blockHashAtH3C)); [self updateStoreWithMasternodeList:masternodeListAtH3C addedMasternodes:mnListDiffResultAtH3C.addedMasternodes modifiedMasternodes:mnListDiffResultAtH3C.modifiedMasternodes addedQuorums:mnListDiffResultAtH3C.addedQuorums completion:^(NSError *error) {}]; } if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH2C skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH2C count] || ![self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH2C]) { + } else if (![missingMasternodeListsAtH2C count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH2C]) { DSLog(@"••• updateStoreWithMasternodeList (h-2c): %u: %@ (%@)", masternodeListAtH2C.height, uint256_hex(blockHashAtH2C), uint256_reverse_hex(blockHashAtH2C)); [self updateStoreWithMasternodeList:masternodeListAtH2C addedMasternodes:mnListDiffResultAtH2C.addedMasternodes modifiedMasternodes:mnListDiffResultAtH2C.modifiedMasternodes addedQuorums:mnListDiffResultAtH2C.addedQuorums completion:^(NSError *error) {}]; } if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtHC skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtHC count] || ![self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtHC]) { + } else if (![missingMasternodeListsAtHC count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtHC]) { DSLog(@"••• updateStoreWithMasternodeList (h-c): %u: %@ (%@)", masternodeListAtHC.height, uint256_hex(blockHashAtHC), uint256_reverse_hex(blockHashAtHC)); [self updateStoreWithMasternodeList:masternodeListAtHC addedMasternodes:mnListDiffResultAtHC.addedMasternodes modifiedMasternodes:mnListDiffResultAtHC.modifiedMasternodes addedQuorums:mnListDiffResultAtHC.addedQuorums completion:^(NSError *error) {}]; } if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtH skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![missingMasternodeListsAtH count] || ![self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH]) { + } else if (![missingMasternodeListsAtH count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtH]) { DSLog(@"••• updateStoreWithMasternodeList (h): %u: %@ (%@)", masternodeListAtH.height, uint256_hex(blockHashAtH), uint256_reverse_hex(blockHashAtH)); [self updateStoreWithMasternodeList:masternodeListAtH addedMasternodes:mnListDiffResultAtH.addedMasternodes modifiedMasternodes:mnListDiffResultAtH.modifiedMasternodes addedQuorums:mnListDiffResultAtH.addedQuorums completion:^(NSError *error) {}]; } @@ -613,13 +628,15 @@ - (void)processQRInfoResult:(DSQRInfoProcessingResult *)result forPeer:(DSPeer * if (![self.quorumRotationService shouldProcessDiffResult:mnListDiffResultAtTip skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; } else { - if ([missingMasternodeListsAtTip count] && [self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtTip]) { + if ([missingMasternodeListsAtTip count] && [masternodeListQueriesNeedingQuorumsValidated containsObject:blockHashDataAtTip]) { [self.quorumRotationService removeFromRetrievalQueue:blockHashDataAtTip]; [self processMissingMasternodeLists:missingMasternodeLists forMasternodeList:masternodeListAtTip]; } else { if (uint256_eq(self.store.lastQueriedBlockHash, blockHashAtTip)) { self.quorumRotationService.currentMasternodeList = masternodeListAtTip; - [self.store.masternodeListQueriesNeedingQuorumsValidated removeObject:blockHashDataAtTip]; + @synchronized (self.store.masternodeListQueriesNeedingQuorumsValidated) { + [self.store.masternodeListQueriesNeedingQuorumsValidated removeObject:blockHashDataAtTip]; + } } DSLog(@"••• updateStoreWithMasternodeList (tip): %u: %@ (%@)", masternodeListAtTip.height, uint256_hex(blockHashAtTip), uint256_reverse_hex(blockHashAtTip)); [self updateStoreWithMasternodeList:masternodeListAtTip addedMasternodes:mnListDiffResultAtTip.addedMasternodes modifiedMasternodes:mnListDiffResultAtTip.modifiedMasternodes addedQuorums:mnListDiffResultAtTip.addedQuorums completion:^(NSError *error) {}]; @@ -639,7 +656,7 @@ - (void)processQRInfoResult:(DSQRInfoProcessingResult *)result forPeer:(DSPeer * NSData *diffBlockHashData = uint256_data(diffBlockHash); if (![self.quorumRotationService shouldProcessDiffResult:diffResult skipPresenceInRetrieval:YES]) { [self.quorumRotationService issueWithMasternodeListFromPeer:peer]; - } else if (![diffResult.neededMissingMasternodeLists count] || ![self.store.masternodeListQueriesNeedingQuorumsValidated containsObject:diffBlockHashData]) { + } else if (![diffResult.neededMissingMasternodeLists count] || ![masternodeListQueriesNeedingQuorumsValidated containsObject:diffBlockHashData]) { [self updateStoreWithMasternodeList:diffMasternodeList addedMasternodes:diffResult.addedMasternodes modifiedMasternodes:diffResult.modifiedMasternodes addedQuorums:diffResult.addedQuorums completion:^(NSError *error) {}]; } } @@ -684,7 +701,9 @@ - (void)updateStoreWithMasternodeList:(DSMasternodeList *)masternodeList addedMa - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message { DSLog(@"•••• -> received mnlistdiff: %@", uint256_hex(message.SHA256)); - self.masternodeListDiffService.timedOutAttempt = 0; + @synchronized (self.masternodeListDiffService) { + self.masternodeListDiffService.timedOutAttempt = 0; + } dispatch_async(self.processingQueue, ^{ dispatch_group_enter(self.processingGroup); DSMasternodeProcessorContext *ctx = [self createDiffMessageContext:self.chain.isTestnet isFromSnapshot:NO isDIP0024:NO peer:peer merkleRootLookup:^UInt256(UInt256 blockHash) { @@ -719,7 +738,9 @@ - (void)peer:(DSPeer *)peer relayedMasternodeDiffMessage:(NSData *)message { - (void)peer:(DSPeer *)peer relayedQuorumRotationInfoMessage:(NSData *)message { DSLog(@"•••• -> received qrinfo: %@", uint256_hex(message.SHA256)); - self.quorumRotationService.timedOutAttempt = 0; + @synchronized (self.quorumRotationService) { + self.quorumRotationService.timedOutAttempt = 0; + } dispatch_async(self.processingQueue, ^{ dispatch_group_enter(self.processingGroup); MerkleRootFinder merkleRootLookup = ^UInt256(UInt256 blockHash) { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h index 58c24aeb8..174409623 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.h @@ -56,6 +56,14 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSPeerManagerFilterDidChangeNotificat UIAlertViewDelegate #endif > +typedef NS_ENUM(uint16_t, DSDisconnectReason) +{ + DSDisconnectReason_StartNewPhase = 0, + DSDisconnectReason_ChainWipe, + DSDisconnectReason_ChainSwitch, + DSDisconnectReason_TrustedPeerSet, + DSDisconnectReason_Error, +}; @property (nonatomic, readonly) BOOL connected; @property (nonatomic, readonly) NSUInteger peerCount; @@ -74,9 +82,9 @@ FOUNDATION_EXPORT NSString *_Nonnull const DSPeerManagerFilterDidChangeNotificat - (void)removeTrustedPeerHost; -- (void)clearPeers; +- (void)clearPeers:(DSDisconnectReason)reason; - (void)connect; -- (void)disconnect; +- (void)disconnect:(DSDisconnectReason)reason; - (void)clearRegisteredPeers; - (void)registerPeerAtLocation:(UInt128)IPAddress port:(uint32_t)port dapiJRPCPort:(uint32_t)dapiJRPCPort dapiGRPCPort:(uint32_t)dapiGRPCPort; diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m index 1b2de112d..09922f025 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSPeerManager.m @@ -254,12 +254,12 @@ + (UInt128)ipAddressFromString:(NSString *)address { } - (void)removeTrustedPeerHost { - [self disconnect]; + [self disconnect:DSDisconnectReason_TrustedPeerSet]; [self setTrustedPeerHost:nil]; } -- (void)clearPeers { - [self disconnect]; +- (void)clearPeers:(DSDisconnectReason)reason { + [self disconnect:reason]; @synchronized(self) { _peers = nil; } @@ -431,6 +431,7 @@ - (void)peerMisbehaving:(DSPeer *)peer errorMessage:(NSString *)errorMessage { } [peer disconnectWithError:[NSError errorWithCode:500 localizedDescriptionKey:errorMessage]]; + DSLog(@"peerMisbehaving -> peerManager::connect"); [self connect]; } } @@ -551,8 +552,7 @@ - (void)setTrustedPeerHost:(NSString *_Nullable)host { if (!host) [[NSUserDefaults standardUserDefaults] removeObjectForKey:[self settingsFixedPeerKey]]; else - [[NSUserDefaults standardUserDefaults] setObject:host - forKey:[self settingsFixedPeerKey]]; + [[NSUserDefaults standardUserDefaults] setObject:host forKey:[self settingsFixedPeerKey]]; } // MARK: - Peer Registration @@ -566,6 +566,7 @@ - (void)resumeBlockchainSynchronizationOnPeers { if (self.downloadPeer) { [self updateFilterOnPeers]; } else { + DSLog(@"resumeBlockchainSynchronizationOnPeers -> peerManager::connect"); [self connect]; } } @@ -610,7 +611,7 @@ - (void)updateFilterOnPeers { // MARK: - Peer Registration - (void)clearRegisteredPeers { - [self clearPeers]; + [self clearPeers:DSDisconnectReason_ChainWipe]; setKeychainArray(@[], self.chain.registeredPeersKey, NO); } @@ -673,7 +674,7 @@ - (void)useMasternodeList:(DSMasternodeList *)masternodeList withConnectivityNon if (!_peers) { _peers = [NSMutableOrderedSet orderedSetWithArray:peers]; } else { - [self clearPeers]; + [self clearPeers:DSDisconnectReason_StartNewPhase]; _peers = [NSMutableOrderedSet orderedSetWithArray:peers]; [self.peers minusSet:self.misbehavingPeers]; } @@ -683,6 +684,7 @@ - (void)useMasternodeList:(DSMasternodeList *)masternodeList withConnectivityNon if (peers.count > 1 && peers.count < 1000) [self savePeers]; // peer relaying is complete when we receive <1000 if (connected) { + DSLog(@"useMasternodeList -> peerManager::connect"); [self connect]; } dispatch_async(dispatch_get_main_queue(), ^{ @@ -700,43 +702,55 @@ - (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 - - if (self.chainManager.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]; - }]; + @synchronized (self.chainManager) { + if (self.chainManager.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 } -#endif - } - - if (self.chainManager.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]; - }]; + if (self.chainManager.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 } -#endif } - +// @synchronized(self.mutableConnectedPeers) { +// [self.mutableConnectedPeers minusSet:[self.connectedPeers objectsPassingTest:^BOOL(id obj, BOOL *stop) { +// return ([obj status] == DSPeerStatus_Disconnected) ? YES : NO; +// }]]; +// } @synchronized(self.mutableConnectedPeers) { - [self.mutableConnectedPeers minusSet:[self.connectedPeers objectsPassingTest:^BOOL(id obj, BOOL *stop) { - return ([obj status] == DSPeerStatus_Disconnected) ? YES : NO; - }]]; + NSMutableSet *disconnectedPeers = [NSMutableSet set]; + for (DSPeer *peer in self.mutableConnectedPeers) { +// @synchronized(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 @@ -781,11 +795,12 @@ - (void)connect { }); } -- (void)disconnect { +- (void)disconnect:(DSDisconnectReason)reason { self.desiredState = DSPeerManagerDesiredState_Disconnected; dispatch_async(self.networkingQueue, ^{ - for (DSPeer *peer in self.connectedPeers) { + if (reason != DSDisconnectReason_StartNewPhase) self.connectFailures = MAX_CONNECT_FAILURES; // prevent futher automatic reconnect attempts + for (DSPeer *peer in self.connectedPeers) { [peer disconnect]; } }); @@ -802,15 +817,18 @@ - (void)disconnectDownloadPeerForError:(NSError *)error withCompletion:(void (^_ } - (void)syncTimeout { - NSTimeInterval now = [NSDate timeIntervalSince1970]; - - if (now - self.chainManager.lastChainRelayTime < 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 - (now - self.chainManager.lastChainRelayTime)]; - return; + @synchronized (self.chainManager) { + 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]; + return; + } } + [self disconnectDownloadPeerForError:[NSError errorWithCode:500 descriptionKey:DSLocalizedString(@"Synchronization Timeout", @"An error message for notifying that chain sync has timed out")] withCompletion:nil]; } @@ -931,28 +949,30 @@ - (void)peerConnected:(DSPeer *)peer { [self.downloadPeer disconnect]; self.downloadPeer = bestPeer; - _connected = YES; + @synchronized (self) { + _connected = YES; + } if ([self.chain syncsBlockchain] && [self.chain canConstructAFilter]) { [bestPeer sendFilterloadMessage:[self.transactionManager transactionsBloomFilterForPeer:bestPeer].data]; } bestPeer.currentBlockHeight = self.chain.lastSyncBlockHeight; - [self.chainManager assignSyncWeights]; - if ([self.chain syncsBlockchain] && ((self.chain.lastSyncBlockHeight != self.chain.lastTerminalBlockHeight) || (self.chain.lastSyncBlockHeight < bestPeer.lastBlockHeight))) { // start blockchain sync - [self.chainManager resetLastRelayedItemTime]; - dispatch_async(dispatch_get_main_queue(), ^{ // setup a timer to detect if the sync stalls - [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]; - }); - } else { // we're already synced - [self.chainManager chainFinishedSyncingTransactionsAndBlocks:self.chain fromPeer:nil onMainChain:TRUE]; - } - dispatch_async(dispatch_get_main_queue(), ^{ + 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]; + } else { // we're already synced + [self.chainManager chainFinishedSyncingTransactionsAndBlocks:self.chain fromPeer:nil onMainChain:TRUE]; + } [[NSNotificationCenter defaultCenter] postNotificationName:DSPeerManagerConnectedPeersDidChangeNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain}]; @@ -964,9 +984,10 @@ - (void)peerConnected:(DSPeer *)peer { - (void)peer:(DSPeer *)peer disconnectedWithError:(NSError *)error { DSLog(@"%@:%d disconnected%@%@", peer.host, peer.port, (error ? @", " : @""), (error ? error : @"")); - + BOOL banned = NO; if ([error.domain isEqual:@"DashSync"]) { //} && error.code != DASH_PEER_TIMEOUT_CODE) { [self peerMisbehaving:peer errorMessage:error.localizedDescription]; // if it's protocol error other than timeout, the peer isn't following the rules + banned = YES; } else if (error) { // timeout or some non-protocol related network error [self.peers removeObject:peer]; self.connectFailures++; @@ -981,7 +1002,7 @@ - (void)peer:(DSPeer *)peer disconnectedWithError:(NSError *)error { if (self.connectFailures > MAX_CONNECT_FAILURES) self.connectFailures = MAX_CONNECT_FAILURES; } - if (!self.connected && self.connectFailures == MAX_CONNECT_FAILURES) { + if (!self.connected && self.connectFailures >= MAX_CONNECT_FAILURES) { [self chainSyncStopped]; // clear out stored peers so we get a fresh list from DNS on next connect attempt @@ -1008,7 +1029,8 @@ - (void)peer:(DSPeer *)peer disconnectedWithError:(NSError *)error { #if TARGET_OS_IOS if ((self.desiredState == DSPeerManagerDesiredState_Connected) && (self.terminalHeadersSaveTaskId != UIBackgroundTaskInvalid || [UIApplication sharedApplication].applicationState != UIApplicationStateBackground)) { - [self connect]; // try connecting to another peer + DSLog(@"peer disconnectedWithError -> peerManager::connect"); + if (!banned) [self connect]; // try connecting to another peer } #else if (self.desiredState == DSPeerManagerDesiredState_Connected) { diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m index 15da1ffbb..5689e075f 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSSporkManager.m @@ -94,7 +94,7 @@ - (BOOL)instantSendActive { - (BOOL)sporksUpdatedSignatures { DSSpork *updateSignatureSpork = self.sporkDictionary[@(DSSporkIdentifier_Spork6NewSigs)]; if (!updateSignatureSpork) return FALSE; //assume false - return updateSignatureSpork.value <= self.chain.lastTerminalBlockHeight; + return updateSignatureSpork.value <= self.chain.lastTerminalBlock.height; } - (BOOL)deterministicMasternodeListEnabled { @@ -122,7 +122,9 @@ - (BOOL)chainLocksEnabled { } - (NSDictionary *)sporkDictionary { - return [_mSporkDictionary copy]; + @synchronized (self) { + return [_mSporkDictionary copy]; + } } // MARK: - Spork Sync @@ -171,12 +173,16 @@ - (void)peer:(DSPeer *_Nonnull)peer hasSporkHashes:(NSSet *_Nonnull)sporkHashes if (hasNew) [self getSporks]; } -- (void)peer:(DSPeer *)peer relayedSpork:(DSSpork *)spork { +- (void)peer:(DSPeer *)peer relayedSpork:(NSData *)message { + DSSpork *spork = [DSSpork sporkWithMessage:message onChain:self.chain]; + DSLog(@"received spork %u (%@) with message %@", spork.identifier, spork.identifierString, message.hexString); if (!spork.isValid) { [self.peerManager peerMisbehaving:peer errorMessage:@"Spork is not valid"]; return; } - self.lastSyncedSporks = [NSDate timeIntervalSince1970]; + @synchronized (self) { + self.lastSyncedSporks = [NSDate timeIntervalSince1970]; + } DSSpork *currentSpork = self.sporkDictionary[@(spork.identifier)]; BOOL updatedSpork = FALSE; __block NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; @@ -195,8 +201,7 @@ - (void)peer:(DSPeer *)peer relayedSpork:(DSSpork *)spork { [self setSporkValue:spork forKeyIdentifier:spork.identifier]; } if (!currentSpork || updatedSpork) { - [dictionary setObject:spork - forKey:@"new"]; + [dictionary setObject:spork forKey:@"new"]; [dictionary setObject:self.chain forKey:DSChainManagerNotificationChainKey]; [self.managedObjectContext performBlockAndWait:^{ @autoreleasepool { @@ -206,8 +211,7 @@ - (void)peer:(DSPeer *)peer relayedSpork:(DSSpork *)spork { if (!sporkEntity) { sporkEntity = [DSSporkEntity managedObjectInBlockedContext:self.managedObjectContext]; } - [sporkEntity setAttributesFromSpork:spork - withSporkHash:hashEntity]; // add new peers + [sporkEntity setAttributesFromSpork:spork withSporkHash:hashEntity]; // add new peers [self.managedObjectContext ds_save]; } else { DSLog(@"Spork was received that wasn't requested"); diff --git a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m index ba2c5bb31..58cbb9e8d 100644 --- a/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m +++ b/DashSync/shared/Models/Managers/Chain Managers/DSTransactionManager.m @@ -70,6 +70,7 @@ #define MAX_TOTAL_TRANSACTIONS_FOR_BLOOM_FILTER_RETARGETING 500 #define SAVE_MAX_TRANSACTIONS_INFO (DEBUG && 0) +#define DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS (DEBUG && 0) @interface DSTransactionManager () @@ -885,8 +886,7 @@ - (void)fetchMempoolFromPeer:(DSPeer *)peer { [peer sendFilterloadMessage:[self transactionsBloomFilterForPeer:peer].data]; } - [peer sendInvMessageForHashes:self.publishedTx.allKeys - ofType:DSInvType_Tx]; // publish pending tx + [peer sendInvMessageForHashes:self.publishedTx.allKeys ofType:DSInvType_Tx]; // publish pending tx [peer sendPingMessageWithPongHandler:^(BOOL success) { if (success) { DSLog(@"[DSTransactionManager] fetching mempool ping success peer %@", peer.host); @@ -1143,8 +1143,7 @@ - (DSTransaction *)peer:(DSPeer *)peer requestedTransaction:(UInt256)txHash { #define TEST_NO_RELAY (0 && !DEBUG) -- (void)peer:(DSPeer *)peer hasTransactionWithHash:(UInt256)txHash; -{ +- (void)peer:(DSPeer *)peer hasTransactionWithHash:(UInt256)txHash { #if TEST_NO_RELAY return; #endif @@ -1751,7 +1750,7 @@ - (void)checkChainLocksWaitingForQuorums { [[NSNotificationCenter defaultCenter] postNotificationName:DSChainBlockWasLockedNotification object:nil userInfo:@{DSChainManagerNotificationChainKey: self.chain, DSChainNotificationBlockKey: block}]; }); } else { -#if DEBUG +#if DEBUG_CHAIN_LOCKS_WAITING_FOR_QUORUMS DSMasternodeList *masternodeList = nil; DSQuorumEntry *quorum = [chainLock findSigningQuorumReturnMasternodeList:&masternodeList]; if (quorum && masternodeList) { diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListService.m b/DashSync/shared/Models/Masternode/DSMasternodeListService.m index a4af718a9..0c1546362 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListService.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListService.m @@ -56,27 +56,37 @@ - (instancetype)initWithChain:(DSChain *)chain store:(DSMasternodeListStore *)st } - (void)startTimeOutObserver { - __block NSSet *requestsInRetrieval = [self.requestsInRetrieval copy]; - __block NSUInteger masternodeListCount = [self.store knownMasternodeListsCount]; - self.timeOutObserverTry++; - __block uint16_t timeOutObserverTry = self.timeOutObserverTry; - dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * (self.timedOutAttempt + 1) * NSEC_PER_SEC)); + __block NSSet *requestsInRetrieval; + __block NSUInteger masternodeListCount; + __block uint16_t timeOutObserverTry; + dispatch_time_t timeout; + @synchronized (self) { + requestsInRetrieval = [self.requestsInRetrieval copy]; + masternodeListCount = [self.store knownMasternodeListsCount]; + self.timeOutObserverTry++; + timeOutObserverTry = self.timeOutObserverTry; + timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * (self.timedOutAttempt + 1) * NSEC_PER_SEC)); + } dispatch_after(timeout, self.chain.networkingQueue, ^{ - if (!self.retrievalQueueMaxAmount || self.timeOutObserverTry != timeOutObserverTry) { - return; - } - // Removes from the receiving set each object that isn’t a member of another given set. - NSMutableSet *leftToGet = [requestsInRetrieval mutableCopy]; - [leftToGet intersectSet:self.requestsInRetrieval]; - - if ((masternodeListCount == [self.store knownMasternodeListsCount]) && [requestsInRetrieval isEqualToSet:leftToGet]) { - DSLog(@"TimedOut"); - self.timedOutAttempt++; - [self disconnectFromDownloadPeer]; - [self cleanRequestsInRetrieval]; - [self dequeueMasternodeListRequest]; - } else { - [self startTimeOutObserver]; + __block NSSet *requestsInRetrieval2; + __block NSUInteger masternodeListCount2; + @synchronized (self) { + if (!self.retrievalQueueMaxAmount || self.timeOutObserverTry != timeOutObserverTry) { + return; + } + requestsInRetrieval2 = [self.requestsInRetrieval copy]; + // Removes from the receiving set each object that isn’t a member of another given set. + NSMutableSet *leftToGet = [requestsInRetrieval mutableCopy]; + [leftToGet intersectSet:requestsInRetrieval2]; + if ((masternodeListCount == [self.store knownMasternodeListsCount]) && [requestsInRetrieval isEqualToSet:leftToGet]) { + DSLog(@"TimedOut"); + self.timedOutAttempt++; + [self disconnectFromDownloadPeer]; + [self cleanRequestsInRetrieval]; + [self dequeueMasternodeListRequest]; + } else { + [self startTimeOutObserver]; + } } }); } @@ -101,7 +111,6 @@ - (void)composeMasternodeListRequest:(NSOrderedSet *)list { - (void)dequeueMasternodeListRequest { [self fetchMasternodeListsToRetrieve:^(NSOrderedSet *list) { -// NSLog(@"•••• dequeueMasternodeListRequest with list: (%@)", [self logListSet:list]); [self composeMasternodeListRequest:list]; [self startTimeOutObserver]; }]; @@ -258,7 +267,10 @@ - (NSUInteger)retrievalQueueCount { } - (void)updateMasternodeRetrievalQueue { - self.retrievalQueueMaxAmount = MAX(self.retrievalQueueMaxAmount, self.retrievalQueue.count); + NSUInteger currentCount = self.retrievalQueue.count; + dispatch_async(dispatch_get_main_queue(), ^{ + 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; }]; @@ -274,7 +286,11 @@ - (void)fetchMasternodeListsToRetrieve:(void (^)(NSOrderedSet *listsTo DSLog(@"A masternode list is already in retrieval: %@", self); return; } - if (!self.peerManager.downloadPeer || (self.peerManager.downloadPeer.status != DSPeerStatus_Connected)) { + BOOL peerIsDisconnected; + @synchronized (self.peerManager.downloadPeer) { + peerIsDisconnected = !self.peerManager.downloadPeer || self.peerManager.downloadPeer.status != DSPeerStatus_Connected; + } + if (peerIsDisconnected) { if (self.chain.chainManager.syncPhase != DSChainSyncPhase_Offline) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), self.chain.networkingQueue, ^{ [self fetchMasternodeListsToRetrieve:completion]; @@ -299,16 +315,24 @@ - (DSMasternodeListRequest*__nullable)requestInRetrievalFor:(UInt256)baseBlockHa - (BOOL)removeRequestInRetrievalForBaseBlockHash:(UInt256)baseBlockHash blockHash:(UInt256)blockHash { DSMasternodeListRequest *matchedRequest = [self requestInRetrievalFor:baseBlockHash blockHash:blockHash]; if (!matchedRequest) { + #if DEBUG + NSSet *requestsInRetrieval; + @synchronized (self.requestsInRetrieval) { + requestsInRetrieval = [self.requestsInRetrieval copy]; + } NSMutableArray *requestsInRetrievalStrings = [NSMutableArray array]; - for (DSMasternodeListRequest *requestInRetrieval in [self.requestsInRetrieval copy]) { + for (DSMasternodeListRequest *requestInRetrieval in requestsInRetrieval) { [requestsInRetrievalStrings addObject:[requestInRetrieval logWithBlockHeightLookup:^uint32_t(UInt256 blockHash) { return [self.store heightForBlockHash:blockHash]; }]]; } - DSLog(@"•••• A masternode list (%u..%u %@ .. %@) was received that is not set to be retrieved (%@)", [self.store heightForBlockHash:baseBlockHash], [self.store heightForBlockHash:blockHash], uint256_hex(baseBlockHash), uint256_hex(blockHash), [requestsInRetrievalStrings componentsJoinedByString:@", "]); - return NO; - } - [self.requestsInRetrieval removeObject:matchedRequest]; + DSLog(@"•••• A masternode list (%@ .. %@) was received that is not set to be retrieved (%@)", uint256_hex(baseBlockHash), uint256_hex(blockHash), [requestsInRetrievalStrings componentsJoinedByString:@", "]); + #endif /* DEBUG */ + return NO; + } + @synchronized (self.requestsInRetrieval) { + [self.requestsInRetrieval removeObject:matchedRequest]; + } return YES; } @@ -337,8 +361,7 @@ - (void)issueWithMasternodeListFromPeer:(DSPeer *)peer { } else if (![faultyPeers containsObject:peer.location]) { faultyPeers = [faultyPeers arrayByAddingObject:peer.location]; } - [[NSUserDefaults standardUserDefaults] setObject:faultyPeers - forKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS]; + [[NSUserDefaults standardUserDefaults] setObject:faultyPeers forKey:CHAIN_FAULTY_DML_MASTERNODE_PEERS]; [self dequeueMasternodeListRequest]; } dispatch_async(dispatch_get_main_queue(), ^{ @@ -349,7 +372,9 @@ - (void)issueWithMasternodeListFromPeer:(DSPeer *)peer { - (void)sendMasternodeListRequest:(DSMasternodeListRequest *)request { // DSLog(@"•••• sendMasternodeListRequest: %@", [request toData].hexString); [self.peerManager sendRequest:request]; - [self.requestsInRetrieval addObject:request]; + @synchronized (self.requestsInRetrieval) { + [self.requestsInRetrieval addObject:request]; + } } @end diff --git a/DashSync/shared/Models/Masternode/DSMasternodeListStore.m b/DashSync/shared/Models/Masternode/DSMasternodeListStore.m index 4e965dfdc..bfe90dd4a 100644 --- a/DashSync/shared/Models/Masternode/DSMasternodeListStore.m +++ b/DashSync/shared/Models/Masternode/DSMasternodeListStore.m @@ -95,40 +95,54 @@ - (NSArray *)recentMasternodeLists { } - (NSUInteger)knownMasternodeListsCount { - NSMutableSet *masternodeListHashes = [NSMutableSet setWithArray:self.masternodeListsByBlockHash.allKeys]; - [masternodeListHashes addObjectsFromArray:[self.masternodeListsBlockHashStubs allObjects]]; - return [masternodeListHashes count]; + @synchronized (self.masternodeListsByBlockHash) { + @synchronized (self.masternodeListsBlockHashStubs) { + NSMutableSet *masternodeListHashes = [NSMutableSet setWithArray:self.masternodeListsByBlockHash.allKeys]; + [masternodeListHashes addObjectsFromArray:[self.masternodeListsBlockHashStubs allObjects]]; + return [masternodeListHashes count]; + } + } } - (uint32_t)earliestMasternodeListBlockHeight { uint32_t earliest = UINT32_MAX; - for (NSData *blockHash in [self.masternodeListsBlockHashStubs copy]) { - earliest = MIN(earliest, [self heightForBlockHash:blockHash.UInt256]); + @synchronized (self.masternodeListsBlockHashStubs) { + for (NSData *blockHash in self.masternodeListsBlockHashStubs) { + earliest = MIN(earliest, [self heightForBlockHash:blockHash.UInt256]); + } } - for (NSData *blockHash in [self.masternodeListsByBlockHash copy]) { - earliest = MIN(earliest, [self heightForBlockHash:blockHash.UInt256]); + @synchronized (self.masternodeListsByBlockHash) { + for (NSData *blockHash in self.masternodeListsByBlockHash) { + earliest = MIN(earliest, [self heightForBlockHash:blockHash.UInt256]); + } } return earliest; } - (uint32_t)lastMasternodeListBlockHeight { uint32_t last = 0; - for (NSData *blockHash in [self.masternodeListsBlockHashStubs copy]) { - last = MAX(last, [self heightForBlockHash:blockHash.UInt256]); + @synchronized (self.masternodeListsBlockHashStubs) { + for (NSData *blockHash in self.masternodeListsBlockHashStubs) { + last = MAX(last, [self heightForBlockHash:blockHash.UInt256]); + } } - for (NSData *blockHash in [self.masternodeListsByBlockHash copy]) { - last = MAX(last, [self heightForBlockHash:blockHash.UInt256]); + @synchronized (self.masternodeListsByBlockHash) { + for (NSData *blockHash in self.masternodeListsByBlockHash) { + last = MAX(last, [self heightForBlockHash:blockHash.UInt256]); + } } return last ? last : UINT32_MAX; } - (uint32_t)heightForBlockHash:(UInt256)blockhash { if (uint256_is_zero(blockhash)) return 0; - NSNumber *cachedHeightNumber = [self.cachedBlockHashHeights objectForKey:uint256_data(blockhash)]; - if (cachedHeightNumber) return [cachedHeightNumber intValue]; - uint32_t chainHeight = [self.chain heightForBlockHash:blockhash]; - if (chainHeight != UINT32_MAX) [self.cachedBlockHashHeights setObject:@(chainHeight) forKey:uint256_data(blockhash)]; - return chainHeight; + @synchronized (self.cachedBlockHashHeights) { + NSNumber *cachedHeightNumber = [self.cachedBlockHashHeights objectForKey:uint256_data(blockhash)]; + if (cachedHeightNumber) return [cachedHeightNumber intValue]; + uint32_t chainHeight = [self.chain heightForBlockHash:blockhash]; + if (chainHeight != UINT32_MAX) [self.cachedBlockHashHeights setObject:@(chainHeight) forKey:uint256_data(blockhash)]; + return chainHeight; + } } - (UInt256)closestKnownBlockHashForBlockHash:(UInt256)blockHash { @@ -189,8 +203,15 @@ - (BOOL)hasBlockForBlockHash:(NSData *)blockHashData { - (BOOL)hasMasternodeListAt:(NSData *)blockHashData { - // DSLog(@"We already have this masternodeList %@ (%u)", blockHashData.reverse.hexString, [self heightForBlockHash:blockHash]); - return [self.masternodeListsByBlockHash objectForKey:blockHashData] || [self.masternodeListsBlockHashStubs containsObject:blockHashData]; + BOOL hasList; + @synchronized (self.masternodeListsByBlockHash) { + hasList = [self.masternodeListsByBlockHash objectForKey:blockHashData]; + } + BOOL hasStub; + @synchronized (self.masternodeListsBlockHashStubs) { + hasStub = [self.masternodeListsBlockHashStubs containsObject:blockHashData]; + } + return hasList || hasStub; } - (BOOL)hasMasternodeListCurrentlyBeingSaved { @@ -235,8 +256,12 @@ - (DSMasternodeList *)loadMasternodeListAtBlockHash:(NSData *)blockHash withBloc masternodeList = [masternodeListEntity masternodeListWithSimplifiedMasternodeEntryPool:[simplifiedMasternodeEntryPool copy] quorumEntryPool:quorumEntryPool withBlockHeightLookup:blockHeightLookup]; if (masternodeList) { DSLog(@"••• addMasternodeList (loadMasternodeListAtBlockHash) -> %@: %@", blockHash.hexString, masternodeList); - [self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHash]; - [self.masternodeListsBlockHashStubs removeObject:blockHash]; + @synchronized (self.masternodeListsByBlockHash) { + [self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHash]; + } + @synchronized (self.masternodeListsByBlockHash) { + [self.masternodeListsBlockHashStubs removeObject:blockHash]; + } DSLog(@"Loading Masternode List at height %u for blockHash %@ with %lu entries", masternodeList.height, uint256_hex(masternodeList.blockHash), (unsigned long)masternodeList.simplifiedMasternodeEntries.count); } }]; @@ -259,9 +284,6 @@ - (DSMasternodeList *)loadMasternodeListsWithBlockHeightLookup:(BlockHeightFinde if ((i == masternodeListEntities.count - 1) || ((self.masternodeListsByBlockHash.count < 3) && (neededMasternodeListHeight >= masternodeListEntity.block.height))) { //either last one or there are less than 3 (we aim for 3) //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]; -// [masternodeList saveToJsonFileExtended:[NSString stringWithFormat:@"MNLIST_ext_%@_%@_%@.json", @(masternodeList.height), @([[NSDate date] timeIntervalSince1970]), @"loadMasternodeListsWithBlockHeightLookup"]]; - - DSLog(@"••• addMasternodeList (loadMasternodeListsWithBlockHeightLookup) -> %@: %@", uint256_hex(masternodeList.blockHash), masternodeList.debugDescription); [self.masternodeListsByBlockHash setObject:masternodeList forKey:uint256_data(masternodeList.blockHash)]; [self.cachedBlockHashHeights setObject:@(masternodeListEntity.block.height) forKey:uint256_data(masternodeList.blockHash)]; [simplifiedMasternodeEntryPool addEntriesFromDictionary:masternodeList.simplifiedMasternodeListDictionaryByReversedRegistrationTransactionHash]; @@ -286,7 +308,11 @@ - (DSMasternodeList *_Nullable)masternodeListBeforeBlockHash:(UInt256)blockHash uint32_t minDistance = UINT32_MAX; uint32_t blockHeight = [self heightForBlockHash:blockHash]; DSMasternodeList *closestMasternodeList = nil; - NSDictionary *lists = [self.masternodeListsByBlockHash copy]; + NSDictionary *lists; + @synchronized (self.masternodeListsByBlockHash) { + lists = [self.masternodeListsByBlockHash copy]; + } + for (NSData *blockHashData in lists) { uint32_t masternodeListBlockHeight = [self heightForBlockHash:blockHashData.UInt256]; if (blockHeight <= masternodeListBlockHeight) continue; @@ -314,8 +340,12 @@ - (DSMasternodeList *)masternodeListForBlockHash:(UInt256)blockHash withBlockHei - (void)removeAllMasternodeLists { DSLog(@"••• removeAllMasternodeLists -> "); - [self.masternodeListsByBlockHash removeAllObjects]; - [self.masternodeListsBlockHashStubs removeAllObjects]; + @synchronized (self.masternodeListsByBlockHash) { + [self.masternodeListsByBlockHash removeAllObjects]; + } + @synchronized (self.masternodeListsBlockHashStubs) { + [self.masternodeListsBlockHashStubs removeAllObjects]; + } self.masternodeListAwaitingQuorumValidation = nil; } @@ -335,7 +365,9 @@ - (void)removeOldMasternodeLists:(uint32_t)lastBlockHeight { //A quorum references the masternode list by it's block //we need to check if this masternode list is being referenced by a quorum using the inverse of quorum.block.masternodeList [self.managedObjectContext deleteObject:masternodeListEntity]; - [self.masternodeListsByBlockHash removeObjectForKey:masternodeListEntity.block.blockHash]; + @synchronized (self.masternodeListsByBlockHash) { + [self.masternodeListsByBlockHash removeObjectForKey:masternodeListEntity.block.blockHash]; + } } if (removedItems) { //Now we should delete old quorums @@ -407,10 +439,9 @@ - (void)saveMasternodeList:(DSMasternodeList *)masternodeList addedMasternodes:( } NSArray *updatedSimplifiedMasternodeEntries = [addedMasternodes.allValues arrayByAddingObjectsFromArray:modifiedMasternodes.allValues]; [self.chain updateAddressUsageOfSimplifiedMasternodeEntries:updatedSimplifiedMasternodeEntries]; - DSLog(@"••• addMasternodeList -> %@: %@", blockHashData.hexString, masternodeList); -// [masternodeList saveToJsonFileExtended:[NSString stringWithFormat:@"MNLIST_ext_%@_%@_%@.json", @(masternodeList.height), @([[NSDate date] timeIntervalSince1970]), @"saveMasternodeList"]]; - - [self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHashData]; + @synchronized (self.masternodeListsByBlockHash) { + [self.masternodeListsByBlockHash setObject:masternodeList forKey:blockHashData]; + } [self notifyMasternodeListUpdate]; dispatch_group_enter(self.savingGroup); //We will want to create unknown blocks if they came from insight @@ -737,7 +768,9 @@ - (BOOL)addBlockToValidationQueue:(DSMerkleBlock *)merkleBlock { return NO; } self.lastQueriedBlockHash = merkleBlockHash; - [self.masternodeListQueriesNeedingQuorumsValidated addObject:merkleBlockHashData]; + @synchronized (self.masternodeListQueriesNeedingQuorumsValidated) { + [self.masternodeListQueriesNeedingQuorumsValidated addObject:merkleBlockHashData]; + } return YES; } diff --git a/DashSync/shared/Models/Masternode/DSQuorumRotationService.m b/DashSync/shared/Models/Masternode/DSQuorumRotationService.m index e4810a15d..cf0196604 100644 --- a/DashSync/shared/Models/Masternode/DSQuorumRotationService.m +++ b/DashSync/shared/Models/Masternode/DSQuorumRotationService.m @@ -103,31 +103,13 @@ - (void)requestQuorumRotationInfo:(UInt256)previousBlockHash forBlockHash:(UInt2 // blockHeight % dkgInterval == activeSigningQuorumsCount + 11 + 8 DSMasternodeListRequest *matchedRequest = [self requestInRetrievalFor:previousBlockHash blockHash:blockHash]; if (matchedRequest) { - NSLog(@"•••• qrinfo request with such a range already in retrieval: %u..%u %@ .. %@", [self.store heightForBlockHash:previousBlockHash], [self.store heightForBlockHash:blockHash], uint256_hex(previousBlockHash), uint256_hex(blockHash)); + DSLog(@"•••• qrinfo request with such a range already in retrieval: %@ .. %@", uint256_hex(previousBlockHash), uint256_hex(blockHash)); return; } NSArray *baseBlockHashes = @[[NSData dataWithUInt256:previousBlockHash]]; DSGetQRInfoRequest *request = [DSGetQRInfoRequest requestWithBaseBlockHashes:baseBlockHashes blockHash:blockHash extraShare:YES]; - NSLog(@"•••• requestQuorumRotationInfo: %u..%u %@ .. %@", [self.store heightForBlockHash:previousBlockHash], [self.store heightForBlockHash:blockHash], uint256_hex(previousBlockHash), uint256_hex(blockHash)); + DSLog(@"•••• requestQuorumRotationInfo: %@ .. %@", uint256_hex(previousBlockHash), uint256_hex(blockHash)); [self sendMasternodeListRequest:request]; } -- (void)requestQuorumRotationInfo2:(NSArray *)previousBlockHashes forBlockHash:(UInt256)blockHash { - // TODO: optimize qrinfo request queue (up to 4 blocks simultaneously, so we'd make masternodeListsToRetrieve.count%4) - // blockHeight % dkgInterval == activeSigningQuorumsCount + 11 + 8 -// DSMasternodeListRequest *matchedRequest = [self requestInRetrievalFor:previousBlockHash blockHash:blockHash]; -// if (matchedRequest) { -// NSLog(@"•••• qrinfo request with such a range already in retrieval: %u..%u %@ .. %@", self.blockHeightLookup(previousBlockHash), self.blockHeightLookup(blockHash), uint256_hex(previousBlockHash), uint256_hex(blockHash)); -// return; -// } -// NSArray *baseBlockHashes = @[[NSData dataWithUInt256:previousBlockHash]]; -// NSMutableSet *log = [NSMutableSet set]; - NSMutableString *log = [NSMutableString stringWithFormat:@""]; - for (NSData *baseBlockHashData in previousBlockHashes) { - [log appendString:[NSString stringWithFormat:@"%u, ", [self.store heightForBlockHash:baseBlockHashData.UInt256]]]; - } - DSGetQRInfoRequest *request = [DSGetQRInfoRequest requestWithBaseBlockHashes:previousBlockHashes blockHash:blockHash extraShare:YES]; - NSLog(@"•••• requestQuorumRotationInfo: %@: .. %d", log, [self.store heightForBlockHash:blockHash]); - [self sendMasternodeListRequest:request]; -} @end diff --git a/DashSync/shared/Models/Network/DSPeer.h b/DashSync/shared/Models/Network/DSPeer.h index f43063b80..db8421e13 100644 --- a/DashSync/shared/Models/Network/DSPeer.h +++ b/DashSync/shared/Models/Network/DSPeer.h @@ -235,7 +235,7 @@ typedef void (^MempoolCompletionBlock)(BOOL success, BOOL needed, BOOL interrupt @protocol DSPeerSporkDelegate @required -- (void)peer:(DSPeer *)peer relayedSpork:(DSSpork *)spork; +- (void)peer:(DSPeer *)peer relayedSpork:(NSData *)message; - (void)peer:(DSPeer *)peer hasSporkHashes:(NSSet *)sporkHashes; @end diff --git a/DashSync/shared/Models/Network/DSPeer.m b/DashSync/shared/Models/Network/DSPeer.m index caee03c48..ebd8ed663 100644 --- a/DashSync/shared/Models/Network/DSPeer.m +++ b/DashSync/shared/Models/Network/DSPeer.m @@ -308,6 +308,7 @@ - (void)disconnect { } - (void)disconnectWithError:(NSError *)error { + if (_status == DSPeerStatus_Disconnected) return; if (!error) { DSLog(@"Disconnected from peer %@ (%@ protocol %d) with no error", self.host, self.useragent, self.version); @@ -317,7 +318,6 @@ - (void)disconnectWithError:(NSError *)error { [NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel connect timeout _status = DSPeerStatus_Disconnected; - if (self.reachabilityObserver) { self.reachability = nil; [[NSNotificationCenter defaultCenter] removeObserver:self.reachabilityObserver]; @@ -330,19 +330,16 @@ - (void)disconnectWithError:(NSError *)error { CFRunLoopStop([self.runLoop getCFRunLoop]); - _status = DSPeerStatus_Disconnected; - dispatch_async(self.delegateQueue, ^{ - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - while (self.pongHandlers.count) { - ((void (^)(BOOL))self.pongHandlers[0])(NO); - [self.pongHandlers removeObjectAtIndex:0]; - } - - if (self.mempoolTransactionCompletion) self.mempoolTransactionCompletion(NO, YES, YES); - self.mempoolTransactionCompletion = nil; + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + while (self.pongHandlers.count) { + ((void (^)(BOOL))self.pongHandlers[0])(NO); + [self.pongHandlers removeObjectAtIndex:0]; + } + if (self.mempoolTransactionCompletion) self.mempoolTransactionCompletion(NO, YES, YES); + self.mempoolTransactionCompletion = nil; + [self dispatchAsyncInDelegateQueue:^{ [self.peerDelegate peer:self disconnectedWithError:error]; - }); + }]; } - (void)error:(NSString *)message, ... NS_FORMAT_FUNCTION(1, 2) { @@ -360,9 +357,9 @@ - (void)didConnect { [NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel pending handshake timeout _status = DSPeerStatus_Connected; - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ if (self->_status == DSPeerStatus_Connected) [self.peerDelegate peerConnected:self]; - }); + }]; } - (void)receivedOrphanBlock { @@ -460,7 +457,9 @@ - (void)sendVerackMessage { } - (void)sendFilterloadMessage:(NSData *)filter { - self.sentFilter = YES; + @synchronized (self) { + self.sentFilter = YES; + } #if DEBUG DSLogPrivate(@"Sending filter with fingerprint %@ to node %@ %@", [NSData dataWithUInt256:filter.SHA256].shortHexString, self.host, self.peerDelegate.downloadPeer == self ? @"(download peer) " : @""); #else @@ -486,14 +485,16 @@ - (void)sendMempoolMessage:(NSArray *)publishedTxHashes completion:(MempoolCompl #else DSLog(@"%@:%u sendMempoolMessage %@", self.host, self.port, @""); #endif - [self.knownTxHashes addObjectsFromArray:publishedTxHashes]; + @synchronized (self.knownTxHashes) { + [self.knownTxHashes addObjectsFromArray:publishedTxHashes]; + } self.sentMempool = YES; if (completion) { if (self.mempoolTransactionCompletion) { - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ if (self->_status == DSPeerStatus_Connected) completion(NO, NO, NO); - }); + }]; } else { self.mempoolTransactionCompletion = completion; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MEMPOOL_TIMEOUT * NSEC_PER_SEC)), self.delegateQueue, ^{ @@ -576,14 +577,18 @@ - (void)sendGetblocksMessageWithLocators:(NSArray *)locators andHashStop:(UInt25 - (void)sendInvMessageForHashes:(NSArray *)invHashes ofType:(DSInvType)invType { DSLogPrivate(@"%@:%u sending inv message of type %@ hashes count %lu", self.host, self.port, [self nameOfInvMessage:invType], (unsigned long)invHashes.count); NSMutableOrderedSet *hashes = [NSMutableOrderedSet orderedSetWithArray:invHashes]; - [hashes minusOrderedSet:self.knownTxHashes]; + @synchronized (self.knownTxHashes) { + [hashes minusOrderedSet:self.knownTxHashes]; + } if (hashes.count == 0) return; DSInvRequest *request = [DSInvRequest requestWithHashes:hashes ofInvType:invType]; [self sendRequest:request]; switch (invType) { case DSInvType_Tx: - [self.knownTxHashes unionOrderedSet:hashes]; + @synchronized (self.knownTxHashes) { + [self.knownTxHashes unionOrderedSet:hashes]; + } break; case DSInvType_GovernanceObjectVote: [self.knownGovernanceObjectVoteHashes unionOrderedSet:hashes]; @@ -605,13 +610,17 @@ - (void)sendInvMessageForHashes:(NSArray *)invHashes ofType:(DSInvType)invType { - (void)sendTransactionInvMessagesforTransactionHashes:(NSArray *)txInvHashes txLockRequestHashes:(NSArray *)txLockRequestInvHashes { NSMutableOrderedSet *txHashes = txInvHashes ? [NSMutableOrderedSet orderedSetWithArray:txInvHashes] : nil; NSMutableOrderedSet *txLockRequestHashes = txLockRequestInvHashes ? [NSMutableOrderedSet orderedSetWithArray:txLockRequestInvHashes] : nil; - [txHashes minusOrderedSet:self.knownTxHashes]; - [txLockRequestHashes minusOrderedSet:self.knownTxHashes]; + @synchronized (self.knownTxHashes) { + [txHashes minusOrderedSet:self.knownTxHashes]; + [txLockRequestHashes minusOrderedSet:self.knownTxHashes]; + } if (txHashes.count + txLockRequestHashes.count == 0) return; DSTransactionInvRequest *request = [DSTransactionInvRequest requestWithTransactionHashes:txHashes txLockRequestHashes:txLockRequestHashes]; [self sendRequest:request]; - txHashes ? [self.knownTxHashes unionOrderedSet:txHashes] : nil; - txLockRequestHashes ? [self.knownTxHashes unionOrderedSet:txLockRequestHashes] : nil; + @synchronized (self.knownTxHashes) { + txHashes ? [self.knownTxHashes unionOrderedSet:txHashes] : nil; + txLockRequestHashes ? [self.knownTxHashes unionOrderedSet:txLockRequestHashes] : nil; + } } - (void)sendGetdataMessageForTxHash:(UInt256)txHash { @@ -672,22 +681,17 @@ - (void)sendGetaddrMessage { } - (void)sendPingMessageWithPongHandler:(void (^)(BOOL success))pongHandler { - NSMutableData *msg = [NSMutableData data]; - - dispatch_async(self.delegateQueue, ^{ - if (!self.pongHandlers) self.pongHandlers = [NSMutableArray array]; - [self.pongHandlers addObject:(pongHandler) ? [pongHandler copy] : [^(BOOL success) { - } copy]]; - - [msg appendUInt64:self.localNonce]; - self.pingStartTime = [NSDate timeIntervalSince1970]; + if (!self.pongHandlers) self.pongHandlers = [NSMutableArray array]; + [self.pongHandlers addObject:(pongHandler) ? [pongHandler copy] : [^(BOOL success) {} copy]]; + uint64_t localNonce = self.localNonce; + self.pingStartTime = [NSDate timeIntervalSince1970]; #if MESSAGE_LOGGING - DSLog(@"%@:%u sending ping", self.host, self.port); + DSLog(@"%@:%u sending ping", self.host, self.port); #endif - - [self sendRequest:[DSPingRequest requestWithLocalNonce:self.localNonce]]; - }); + [self dispatchAsyncInDelegateQueue:^{ + [self sendRequest:[DSPingRequest requestWithLocalNonce:localNonce]]; + }]; } // re-request blocks starting from blockHash, useful for getting any additional transactions after a bloom filter update @@ -931,10 +935,9 @@ - (void)acceptAddrMessage:(NSData *)message { timestamp:timestamp - 2 * 60 * 60 services:services]]; } - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ if (self->_status == DSPeerStatus_Connected) [self.peerDelegate peer:self relayedPeers:peers]; - }); + }]; } - (void)acceptAddrV2Message:(NSData *)message { @@ -1070,16 +1073,22 @@ - (void)acceptInvMessage:(NSData *)message { } } } - - if ([self.chain syncsBlockchain] && !self.sentFilter && !self.sentMempool && !self.sentGetblocks && (txHashes.count > 0) && !onlyPrivateSendTransactions) { + uint32_t currentHeight; + BOOL isFilterNotLoaded; + @synchronized (self) { + currentHeight = self.currentBlockHeight; + isFilterNotLoaded = !self.sentFilter && !self.sentMempool && !self.sentGetblocks; + } + + if ([self.chain syncsBlockchain] && isFilterNotLoaded && (txHashes.count > 0) && !onlyPrivateSendTransactions) { [self error:@"got tx inv message before loading a filter"]; return; } else if (txHashes.count + instantSendLockHashes.count + instantSendLockDHashes.count > 10000) { // this was happening on testnet, some sort of DOS/spam attack? DSLog(@"%@:%u too many transactions, disconnecting", self.host, self.port); [self disconnect]; // disconnecting seems to be the easiest way to mitigate it return; - } else if (self.currentBlockHeight > 0 && blockHashes.count > 2 && blockHashes.count < 500 && - self.currentBlockHeight + self.knownBlockHashes.count + blockHashes.count < self.lastBlockHeight) { + } else if (currentHeight > 0 && blockHashes.count > 2 && blockHashes.count < 500 && + currentHeight + self.knownBlockHashes.count + blockHashes.count < self.lastBlockHeight) { [self error:@"non-standard inv, %u is fewer block hashes than expected", (int)blockHashes.count]; return; } @@ -1094,27 +1103,27 @@ - (void)acceptInvMessage:(NSData *)message { if (blockHashes.count == 1) self.lastBlockHash = blockHashes[0]; if (blockHashes.count > 0) { // remember blockHashes in case we need to re-request them with an updated bloom filter - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.knownBlockHashes unionOrderedSet:blockHashes]; - while (self.knownBlockHashes.count > MAX_GETDATA_HASHES) { [self.knownBlockHashes removeObjectsInRange:NSMakeRange(0, self.knownBlockHashes.count / 3)]; } - }); + }]; } - - if ([txHashes intersectsOrderedSet:self.knownTxHashes]) { // remove transactions we already have - for (NSValue *hash in txHashes) { - UInt256 h; - if (![self.knownTxHashes containsObject:hash]) continue; - [hash getValue:&h]; - dispatch_async(self.delegateQueue, ^{ - if (self->_status == DSPeerStatus_Connected) [self.transactionDelegate peer:self hasTransactionWithHash:h]; - }); + @synchronized (self.knownTxHashes) { + if ([txHashes intersectsOrderedSet:self.knownTxHashes]) { // remove transactions we already have + for (NSValue *hash in txHashes) { + UInt256 h; + if (![self.knownTxHashes containsObject:hash]) continue; + [hash getValue:&h]; + [self dispatchAsyncInDelegateQueue:^{ + if (self->_status == DSPeerStatus_Connected) [self.transactionDelegate peer:self hasTransactionWithHash:h]; + }]; + } + [txHashes minusOrderedSet:self.knownTxHashes]; } - [txHashes minusOrderedSet:self.knownTxHashes]; + [self.knownTxHashes unionOrderedSet:txHashes]; } - [self.knownTxHashes unionOrderedSet:txHashes]; if (instantSendLockHashes.count > 0) { for (NSValue *hash in instantSendLockHashes) { @@ -1125,10 +1134,9 @@ - (void)acceptInvMessage:(NSData *)message { } [instantSendLockHashes minusOrderedSet:self.knownInstantSendLockHashes]; - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ if (self->_status == DSPeerStatus_Connected) [self.transactionDelegate peer:self hasInstantSendLockHashes:instantSendLockHashes]; - }); + }]; [self.knownInstantSendLockHashes unionOrderedSet:instantSendLockHashes]; } @@ -1143,9 +1151,9 @@ - (void)acceptInvMessage:(NSData *)message { [instantSendLockDHashes minusOrderedSet:self.knownInstantSendLockDHashes]; - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ if (self->_status == DSPeerStatus_Connected) [self.transactionDelegate peer:self hasInstantSendLockDHashes:instantSendLockDHashes]; - }); + }]; [self.knownInstantSendLockDHashes unionOrderedSet:instantSendLockDHashes]; } @@ -1161,10 +1169,9 @@ - (void)acceptInvMessage:(NSData *)message { } [chainLockHashes minusOrderedSet:self.knownChainLockHashes]; - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ if (self->_status == DSPeerStatus_Connected) [self.transactionDelegate peer:self hasChainLockHashes:chainLockHashes]; - }); + }]; [self.knownChainLockHashes unionOrderedSet:chainLockHashes]; } @@ -1246,9 +1253,9 @@ - (void)acceptTxMessage:(NSData *)message { if (tx) { __block DSMerkleBlock *currentBlock = self.currentBlock; - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedTransaction:tx inBlock:currentBlock]; - }); + }]; #if LOG_FULL_TX_MESSAGE #if DEBUG DSLogPrivate(@"%@:%u got tx %@ %@", self.host, self.port, uint256_obj(tx.txHash), message.hexString); @@ -1316,10 +1323,9 @@ - (void)acceptIslockMessage:(NSData *)message { [self error:@"got islock message before loading a filter"]; return; } - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedInstantSendTransactionLock:instantSendTransactionLock]; - }); + }]; } - (void)acceptIsdlockMessage:(NSData *)message { @@ -1343,10 +1349,9 @@ - (void)acceptIsdlockMessage:(NSData *)message { [self error:@"got isdlock message before loading a filter"]; return; } - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedInstantSendTransactionLock:instantSendTransactionLock]; - }); + }]; } // HEADER FORMAT: @@ -1453,9 +1458,9 @@ - (void)acceptHeadersMessage:(NSData *)message { [self error:@"invalid block header %@", uint256_obj(block.blockHash)]; return; } - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedHeader:block]; - }); + }]; } } @@ -1480,8 +1485,7 @@ - (void)acceptGetdataMessage:(NSData *)message { } DSLog(@"%@:%u %@got getdata for %u item%@", self.host, self.port, self.peerDelegate.downloadPeer == self ? @"(download peer)" : @"", (int)count, count == 1 ? @"" : @"s"); - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ NSMutableData *notfound = [NSMutableData data]; for (NSUInteger off = l; off < l + count * 36; off += 36) { @@ -1543,7 +1547,7 @@ - (void)acceptGetdataMessage:(NSData *)message { DSNotFoundRequest *request = [DSNotFoundRequest requestWithData:notfound]; [self sendRequest:request]; } - }); + }]; } - (void)acceptNotfoundMessage:(NSData *)message { @@ -1569,10 +1573,9 @@ - (void)acceptNotfoundMessage:(NSData *)message { [blockHashes addObject:uint256_obj([message UInt256AtOffset:off + sizeof(uint32_t)])]; } } - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedNotFoundMessagesWithTransactionHashes:txHashes andBlockHashes:blockHashes]; - }); + }]; } - (void)acceptPingMessage:(NSData *)message { @@ -1593,9 +1596,12 @@ - (void)acceptPongMessage:(NSData *)message { } else if ([message UInt64AtOffset:0] != self.localNonce) { [self error:@"pong message contained wrong nonce: %llu, expected: %llu", [message UInt64AtOffset:0], self.localNonce]; return; - } else if (!self.pongHandlers.count) { - DSLog(@"%@:%u got unexpected pong", self.host, self.port); - return; + } else { + __block BOOL hasNoHandlers = ![self.pongHandlers count]; + if (hasNoHandlers) { + DSLog(@"%@:%u got unexpected pong", self.host, self.port); + return; + } } if (self.pingStartTime > 1) { @@ -1609,13 +1615,13 @@ - (void)acceptPongMessage:(NSData *)message { #if MESSAGE_LOGGING DSLog(@"%@:%u got pong in %fs", self.host, self.port, self.pingTime); #endif - - dispatch_async(self.delegateQueue, ^{ - if (self->_status == DSPeerStatus_Connected && self.pongHandlers.count) { - ((void (^)(BOOL))self.pongHandlers[0])(YES); - [self.pongHandlers removeObjectAtIndex:0]; - } - }); + if (self->_status == DSPeerStatus_Connected && self.pongHandlers.count) { + void (^handler)(BOOL) = [self.pongHandlers objectAtIndex:0]; + [self.pongHandlers removeObjectAtIndex:0]; + [self dispatchAsyncInDelegateQueue:^{ + handler(YES); + }]; + } } #define SAVE_INCOMING_BLOCKS 0 @@ -1636,21 +1642,21 @@ - (void)acceptMerkleblockMessage:(NSData *)message { //else DSLog(@"%@:%u got merkleblock %@", self.host, self.port, block.blockHash); NSMutableOrderedSet *txHashes = [NSMutableOrderedSet orderedSetWithArray:block.transactionHashes]; - - [txHashes minusOrderedSet:self.knownTxHashes]; + @synchronized (self.knownTxHashes) { + [txHashes minusOrderedSet:self.knownTxHashes]; + } if (txHashes.count > 0) { // wait til we get all the tx messages before processing the block self.currentBlock = block; self.currentBlockTxHashes = txHashes; } else { - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedBlock:block]; - #if SAVE_INCOMING_BLOCKS NSString *fileName = [NSString stringWithFormat:@"%@-%d-%@.block", self.chain.devnetIdentifier, block.height, uint256_hex(block.blockHash)]; [message saveToFile:fileName inDirectory:NSCachesDirectory]; #endif - }); + }]; } } @@ -1673,10 +1679,9 @@ - (void)acceptChainLockMessage:(NSData *)message { [self error:@"got chain lock message before loading a filter"]; return; } - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self relayedChainLock:chainLock]; - }); + }]; } // BIP61: https://github.com/bitcoin/bips/blob/master/bip-0061.mediawiki @@ -1700,9 +1705,9 @@ - (void)acceptRejectMessage:(NSData *)message { reason = nil; // fixes an unused variable warning for non-debug builds if (uint256_is_not_zero(txHash)) { - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self rejectedTransaction:txHash withCode:code]; - }); + }]; } } @@ -1715,18 +1720,15 @@ - (void)acceptFeeFilterMessage:(NSData *)message { _feePerByte = ceilf((float)[message UInt64AtOffset:0] / 1000.0f); DSLog(@"%@:%u got feefilter with rate %llu per Byte", self.host, self.port, self.feePerByte); - - dispatch_async(self.delegateQueue, ^{ + [self dispatchAsyncInDelegateQueue:^{ [self.transactionDelegate peer:self setFeePerByte:self.feePerByte]; - }); + }]; } // MARK: - accept Control - (void)acceptSporkMessage:(NSData *)message { - DSSpork *spork = [DSSpork sporkWithMessage:message onChain:self.chain]; - DSLog(@"received spork %u (%@) with message %@", spork.identifier, spork.identifierString, message.hexString); - [self.sporkDelegate peer:self relayedSpork:spork]; + [self.sporkDelegate peer:self relayedSpork:message]; } // MARK: - accept Masternode @@ -2049,4 +2051,7 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { } } +- (void)dispatchAsyncInDelegateQueue:(void (^)(void))block { + dispatch_async(self.delegateQueue, ^{ block(); }); +} @end diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 97caec045..bb8df6c35 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -586,11 +586,11 @@ PODS: - "!ProtoCompiler-gRPCPlugin (~> 1.0)" - DAPI-GRPC/Messages - gRPC-ProtoRPC - - DashSharedCore (0.4.10) + - DashSharedCore (0.4.11) - DashSync (0.1.0): - CocoaLumberjack (= 3.7.2) - DAPI-GRPC (= 0.22.0-dev.8) - - DashSharedCore (= 0.4.10) + - DashSharedCore (= 0.4.11) - DSDynamicOptions (= 0.1.2) - DWAlertController (= 0.2.1) - TinyCborObjc (= 0.4.6) @@ -657,7 +657,7 @@ PODS: - gRPC/Interface-Legacy (1.49.0): - gRPC-RxLibrary/Interface (= 1.49.0) - KVO-MVVM (0.5.1) - - Protobuf (3.23.4) + - Protobuf (3.24.3) - SDWebImage (5.14.3): - SDWebImage/Core (= 5.14.3) - SDWebImage/Core (5.14.3) @@ -712,8 +712,8 @@ SPEC CHECKSUMS: CocoaImageHashing: 8656031d0899abe6c1c415827de43e9798189c53 CocoaLumberjack: b7e05132ff94f6ae4dfa9d5bce9141893a21d9da DAPI-GRPC: 138d62523bbfe7e88a39896f1053c0bc12390d9f - DashSharedCore: d107a64758acebc53edacc160ad450f539ff33ee - DashSync: 4a9504583105eb2ace98f38f377cb44fe62f5021 + DashSharedCore: d4dc11749f3555702dbe10c563087e6b48399394 + DashSync: 2d80784e399b869aede6d5bd476a149733451651 DSDynamicOptions: 347cc5d2c4e080eb3de6a86719ad3d861b82adfc DWAlertController: 5f4cd8adf90336331c054857f709f5f8d4b16a5b gRPC: 64f36d689b2ecd99c4351f74e6f91347cdc65d9f @@ -721,7 +721,7 @@ SPEC CHECKSUMS: gRPC-ProtoRPC: 1c223e0f1732bb8d0b9e9e0ea60cc0fe995b8e2d gRPC-RxLibrary: 92327f150e11cf3b1c0f52e083944fd9f5cb5d1e KVO-MVVM: 4df3afd1f7ebcb69735458b85db59c4271ada7c6 - Protobuf: c6bc59bbab3d38a71c67f62d7cb7ca8f8ea4eca1 + Protobuf: 970f7ee93a3a08e3cf64859b8efd95ee32b4f87f SDWebImage: 9c36e66c8ce4620b41a7407698dda44211a96764 tinycbor: d4d71dddda1f8392fbb4249f63faf8552f327590 TinyCborObjc: 5204540fb90ff0c40fb22d408fa51bab79d78a80