Skip to content

Commit be4f7c1

Browse files
bfoss765claude
andcommitted
feat: implement Android-style logging system
- Remove all legacy DSLog/DSLogPrivate statements from codebase (70+ files) - Add new Android-style logging macros: DSLogInfo, DSLogDebug, DSLogWarn, DSLogError, DSLogVerbose - Implement DSCurrentThreadName() helper for thread identification - Add DSAndroidStyleLogFormatter for HH:mm:ss timestamp format matching Android/DashJ New logging coverage: - Transaction broadcasting and relay tracking - Transaction reception from peers (with/without blocks) - InstantSend lock reception and verification - ChainLock reception and verification - Peer connection lifecycle (connect, disconnect, version handshake) - Chain sync start/stop/completion Log format: "HH:MM:SS [thread-name] ClassName - message" Log compaction: 5MB max file size, 24hr rolling, 10 files max, gzip compression Also adds CLAUDE.md documentation files for AI agent context. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 753f944 commit be4f7c1

74 files changed

Lines changed: 1181 additions & 1573 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CLAUDE.md‎

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# DashSync iOS
2+
3+
DashSync is a lightweight blockchain client library for iOS/macOS that enables applications to interact with the Dash cryptocurrency network. It supports both Dash Core Network (Layer 1) and Dash Platform (Layer 2).
4+
5+
## Quick Reference
6+
7+
- **Language**: Objective-C with C/C++/Rust interop
8+
- **Build System**: Xcode + CocoaPods
9+
- **Deployment**: iOS 13.0+, macOS 10.15+
10+
- **Pod Name**: `DashSyncPod`
11+
12+
## Build Requirements
13+
14+
```bash
15+
# Install Rust
16+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
17+
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
18+
19+
# Install protobuf and grpc
20+
brew install protobuf grpc cmake
21+
```
22+
23+
## Common Commands
24+
25+
```bash
26+
# Run example project
27+
cd Example && pod install && open DashSync.xcworkspace
28+
29+
# Run tests
30+
cd Example && xcodebuild test -workspace DashSync.xcworkspace -scheme DashSync-Example -destination 'platform=iOS Simulator,name=iPhone 15'
31+
32+
# Update pods
33+
cd Example && pod update
34+
```
35+
36+
## Project Structure
37+
38+
```
39+
DashSync/
40+
├── DashSync/shared/ # Main framework source (cross-platform)
41+
│ ├── Models/ # Core domain models (24 subdirectories)
42+
│ ├── Libraries/ # Utility libraries
43+
│ └── DashSync.xcdatamodeld/ # Core Data model (83 entities)
44+
├── DashSync/iOS/ # iOS-specific code
45+
├── DashSync/macOS/ # macOS-specific code
46+
├── Example/ # Reference app and tests
47+
├── Scripts/ # Build utilities
48+
└── ChainResources/ # Blockchain data files
49+
```
50+
51+
## Architecture
52+
53+
### Two-Layer Design
54+
- **Layer 1 (Core)**: Traditional blockchain - transactions, blocks, masternodes
55+
- **Layer 2 (Platform)**: Decentralized apps - identities, documents, contracts
56+
57+
### Model-Manager Pattern
58+
- **Models**: Data structures (`DSChain`, `DSWallet`, `DSTransaction`)
59+
- **Managers**: Service coordinators (`DSChainManager`, `DSPeerManager`)
60+
61+
### Key Managers
62+
| Manager | Purpose |
63+
|---------|---------|
64+
| `DSChainsManager` | Multi-chain coordinator (singleton) |
65+
| `DSChainManager` | Single chain operations |
66+
| `DSPeerManager` | P2P network connectivity |
67+
| `DSTransactionManager` | Transaction pool |
68+
| `DSMasternodeManager` | Masternode lists & quorums |
69+
| `DSIdentitiesManager` | Blockchain identities |
70+
| `DSGovernanceSyncManager` | Governance data sync |
71+
72+
### Persistence
73+
- **Core Data** with SQLite backend
74+
- 83 entity definitions in `DashSync.xcdatamodeld`
75+
- Custom transformers in `Models/Persistence/Transformers/`
76+
77+
## Code Conventions
78+
79+
### Naming
80+
- All classes prefixed with `DS` (e.g., `DSChain`, `DSWallet`)
81+
- Entities suffixed with `Entity` (e.g., `DSChainEntity`)
82+
- Managers suffixed with `Manager` (e.g., `DSPeerManager`)
83+
84+
### File Organization
85+
- Public headers in main directory
86+
- `+Protected.h` files for subclass-accessible interfaces
87+
- Categories in `Categories/` subdirectories
88+
89+
### Notifications
90+
Event-driven via `NSNotificationCenter`:
91+
- `DSChainBlocksDidFinishSyncingNotification`
92+
- `DSWalletBalanceDidChangeNotification`
93+
- `DSPeerManagerConnectedPeersDidChangeNotification`
94+
95+
## Key Classes
96+
97+
### Chain & Sync
98+
- `DSChain` (3,562 lines) - Central blockchain state manager
99+
- `DSBlock`, `DSMerkleBlock` - Block representations
100+
- `DSChainLock` - Chain lock mechanism
101+
102+
### Wallet
103+
- `DSWallet` - HD wallet management
104+
- `DSAccount` - Account within wallet
105+
- `DSBIP39Mnemonic` - Mnemonic seed handling
106+
- `DSDerivationPath` - BIP32/44 key derivation
107+
108+
### Transactions
109+
- `DSTransaction` - Base transaction class
110+
- `DSCoinbaseTransaction` - Mining rewards
111+
- `DSProviderRegistrationTransaction` - Masternode registration
112+
- `DSQuorumCommitmentTransaction` - Quorum operations
113+
- `DSCreditFundingTransaction` - Platform funding
114+
115+
### Identity & Platform
116+
- `DSBlockchainIdentity` - Dash Platform identity
117+
- `DSBlockchainInvitation` - Contact requests
118+
- `DPContract` - Platform smart contracts
119+
- `DPDocument` - Platform documents
120+
121+
### Privacy
122+
- `DSCoinJoinManager` - CoinJoin mixing coordination
123+
- `DSCoinJoinWrapper` - Protocol implementation
124+
125+
## Network Support
126+
127+
| Network | Purpose |
128+
|---------|---------|
129+
| Mainnet | Production Dash network |
130+
| Testnet | Testing environment |
131+
| Devnet | Development chains |
132+
| Regnet | Local regression testing |
133+
134+
## Testing
135+
136+
Tests located in `Example/Tests/`:
137+
- `DSChainTests.m` - Chain operations
138+
- `DSTransactionTests.m` - Transaction handling
139+
- `DSDeterministicMasternodeListTests.m` - Masternode lists
140+
- `DSCoinJoinSessionTest.m` - Privacy mixing
141+
- `DSDIP14Tests.m` - DIP compliance
142+
143+
## CI/CD Workflows
144+
145+
- `build.yml` - Main CI pipeline
146+
- `test.yml` - Unit tests
147+
- `lint.yml` - Code linting
148+
- `coverage.yml` - Code coverage
149+
- `syncTestMainnet.yml` / `syncTestTestnet.yml` - Network sync tests
150+
151+
## Dependencies
152+
153+
Key CocoaPods:
154+
- **DashSharedCore** - Rust-based cryptographic primitives
155+
- **CocoaLumberjack** - Logging framework
156+
- **DAPI-GRPC** - Decentralized API protocol
157+
- **TinyCborObjc** - CBOR serialization
158+
159+
## Localization
160+
161+
Supports 15+ languages: en, de, es, ja, zh-Hans, zh-Hant-TW, uk, bg, el, it, cs, sk, ko, pl, tr, vi
162+
163+
## Development Workflow
164+
165+
### Commit Policy
166+
- **DO NOT commit changes until the user has tested them**
167+
- Wait for explicit approval before creating commits
168+
- This applies to all code changes, especially logging and behavioral modifications
169+
170+
### Related Repositories
171+
- **DashJ** (Android equivalent): https://github.com/dashpay/dashj
172+
- **Dash Wallet Android**: https://github.com/dashpay/dash-wallet
173+
174+
## External Resources
175+
176+
- [Dash Core Specs](https://dashcore.readme.io/docs)
177+
- [Dash Improvement Proposals](https://github.com/dashpay/dips)
178+
- [Developer Discord](https://discord.com/channels/484546513507188745/614505310593351735)

‎DashSync/shared/Categories/NSData/NSData+Dash.m‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,13 @@ BOOL setKeychainData(NSData *data, NSString *key, BOOL authenticated) {
5555
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
5656

5757
if (status == noErr) return YES;
58-
DSLogPrivate(@"SecItemAdd error: %@", [NSError osStatusErrorWithCode:status].localizedDescription);
5958
return NO;
6059
}
6160

6261
if (!data) {
6362
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
6463

6564
if (status == noErr) return YES;
66-
DSLogPrivate(@"SecItemDelete error: %@", [NSError osStatusErrorWithCode:status].localizedDescription);
6765
return NO;
6866
}
6967

@@ -72,7 +70,6 @@ BOOL setKeychainData(NSData *data, NSString *key, BOOL authenticated) {
7270
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update);
7371

7472
if (status == noErr) return YES;
75-
DSLogPrivate(@"SecItemUpdate error: %@", [NSError osStatusErrorWithCode:status].localizedDescription);
7673
return NO;
7774
}
7875

@@ -89,7 +86,6 @@ BOOL hasKeychainData(NSString *key, NSError **error) {
8986

9087
if (status == errSecItemNotFound) return NO;
9188
if (status == noErr) return YES;
92-
DSLogPrivate(@"SecItemCopyMatching error: %@", [NSError osStatusErrorWithCode:status].localizedDescription);
9389
if (error) *error = [NSError osStatusErrorWithCode:status];
9490
return NO;
9591
}
@@ -104,7 +100,6 @@ BOOL hasKeychainData(NSString *key, NSError **error) {
104100

105101
if (status == errSecItemNotFound) return nil;
106102
if (status == noErr) return CFBridgingRelease(result);
107-
DSLogPrivate(@"SecItemCopyMatching error: %@", [NSError osStatusErrorWithCode:status].localizedDescription);
108103
if (error) *error = [NSError osStatusErrorWithCode:status];
109104
return nil;
110105
}
@@ -164,9 +159,6 @@ BOOL setKeychainDict(NSDictionary *dict, NSString *key, BOOL authenticated) {
164159
]];
165160
set = [set setByAddingObjectsFromArray:classes];
166161
NSDictionary *dictionary = [NSKeyedUnarchiver unarchivedObjectOfClasses:set fromData:d error:error];
167-
if (*error) {
168-
DSLogPrivate(@"error retrieving dictionary from keychain %@", *error);
169-
}
170162
return dictionary;
171163
//}
172164
}
@@ -189,9 +181,6 @@ BOOL setKeychainArray(NSArray *array, NSString *key, BOOL authenticated) {
189181
]];
190182
set = [set setByAddingObjectsFromArray:classes];
191183
NSArray *array = [NSKeyedUnarchiver unarchivedObjectOfClasses:set fromData:d error:error];
192-
if (*error) {
193-
DSLogPrivate(@"error retrieving array from keychain %@", *error);
194-
}
195184
return array;
196185
}
197186
}

‎DashSync/shared/Categories/NSManagedObject+Sugar.m‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ + (NSArray *)fetchObjects:(NSFetchRequest *)request inContext:(NSManagedObjectCo
209209

210210
[context performBlockAndWait:^{
211211
a = [context executeFetchRequest:request error:&error];
212-
if (error) DSLog(@"%s: %@", __func__, error);
213212
}];
214213

215214
return a;
@@ -277,7 +276,6 @@ + (NSUInteger)countObjects:(NSFetchRequest *)request inContext:(NSManagedObjectC
277276

278277
[context performBlockAndWait:^{
279278
count = [context countForFetchRequest:request error:&error];
280-
if (error) DSLog(@"%s: %@", __func__, error);
281279
}];
282280

283281
return count;

‎DashSync/shared/Categories/NSManagedObjectContext+DSSugar.m‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ - (NSError *)ds_save {
7272
#endif
7373
NSError *error = nil;
7474
if (![self save:&error]) { // persist changes
75-
DSLog(@"%s: %@", __func__, error);
7675
#if DEBUG
7776
abort();
7877
#endif

‎DashSync/shared/DashSync.m‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ - (void)wipeBlockchainNonTerminalDataForChain:(DSChain *)chain inContext:(NSMana
209209

210210
- (void)wipeMasternodeDataForChain:(DSChain *)chain inContext:(NSManagedObjectContext *)context {
211211
NSParameterAssert(chain);
212-
DSLog(@"wipeMasternodeDataForChain: %@", chain);
213212
[self stopSyncForChain:chain];
214213
[context performBlockAndWait:^{
215214
DSChainEntity *chainEntity = [chain chainEntityInContext:context];
@@ -317,16 +316,11 @@ - (void)scheduleBackgroundFetch {
317316

318317
NSError *error = nil;
319318
[[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];
320-
if (error) {
321-
DSLog(@"Error scheduling background refresh");
322-
}
323319
}
324320

325321
- (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
326322
DSChainManager *mainnetManager = [[DSChainsManager sharedInstance] mainnetManager];
327323
if (mainnetManager.syncState.chainSyncProgress >= 1.0) {
328-
DSLog(@"Background fetch: already synced");
329-
330324
if (completionHandler) {
331325
completionHandler(UIBackgroundFetchResultNoData);
332326
}
@@ -343,7 +337,6 @@ - (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))com
343337
object:nil
344338
queue:nil
345339
usingBlock:^(NSNotification *note) {
346-
DSLog(@"Background fetch: protected data available");
347340
[[[DSChainsManager sharedInstance] mainnetManager] startSync];
348341
}];
349342

@@ -352,7 +345,6 @@ - (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))com
352345
object:nil
353346
queue:nil
354347
usingBlock:^(NSNotification *note) {
355-
DSLog(@"Background fetch: sync finished");
356348
[self finishBackgroundFetchWithResult:UIBackgroundFetchResultNewData];
357349
}];
358350

@@ -361,11 +353,9 @@ - (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))com
361353
object:nil
362354
queue:nil
363355
usingBlock:^(NSNotification *note) {
364-
DSLog(@"Background fetch: sync failed");
365356
[self finishBackgroundFetchWithResult:UIBackgroundFetchResultFailed];
366357
}];
367358

368-
DSLog(@"Background fetch: starting");
369359
[mainnetManager startSync];
370360

371361
// sync events to the server
@@ -374,8 +364,6 @@ - (void)performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))com
374364

375365
- (void)backgroundFetchTimedOut {
376366
const double syncProgress = [[DSChainsManager sharedInstance] mainnetManager].syncState.chainSyncProgress;
377-
DSLog(@"Background fetch timeout with progress: %f", syncProgress);
378-
379367
const UIBackgroundFetchResult fetchResult = syncProgress > 0.1 ? UIBackgroundFetchResultNewData : UIBackgroundFetchResultFailed;
380368
[self finishBackgroundFetchWithResult:fetchResult];
381369

‎DashSync/shared/Libraries/AdvancedOperations/Operations/DSOperation.m‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@ - (void)start {
259259
their readiness state.
260260
*/
261261
- (void)execute {
262-
DSLog(@"%@ must override `execute`.", NSStringFromClass(self.class));
263262
[self finish];
264263
}
265264

‎DashSync/shared/Libraries/DSLogger.h‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,28 +25,56 @@ static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
2525
static const DDLogLevel ddLogLevel = DDLogLevelInfo;
2626
#endif /* DEBUG */
2727

28-
#define DSLog(frmt, ...) DDLogInfo(frmt, ##__VA_ARGS__) //!OCLINT
28+
NS_ASSUME_NONNULL_BEGIN
29+
30+
// Thread name helper
31+
NSString *DSCurrentThreadName(void);
32+
33+
#pragma mark - Android-style logging macros
34+
// Format: "HH:MM:SS [thread] ClassName - message"
35+
// These match the Android/DashJ log format
36+
37+
#define DSLogInfo(className, frmt, ...) DDLogInfo(@"[%@] %@ - " frmt, DSCurrentThreadName(), className, ##__VA_ARGS__)
38+
#define DSLogDebug(className, frmt, ...) DDLogDebug(@"[%@] %@ - " frmt, DSCurrentThreadName(), className, ##__VA_ARGS__)
39+
#define DSLogWarn(className, frmt, ...) DDLogWarn(@"[%@] %@ - " frmt, DSCurrentThreadName(), className, ##__VA_ARGS__)
40+
#define DSLogError(className, frmt, ...) DDLogError(@"[%@] %@ - " frmt, DSCurrentThreadName(), className, ##__VA_ARGS__)
41+
42+
#ifdef DEBUG
43+
#define DSLogVerbose(className, frmt, ...) DDLogVerbose(@"[%@] %@ - " frmt, DSCurrentThreadName(), className, ##__VA_ARGS__)
44+
#else
45+
#define DSLogVerbose(className, frmt, ...)
46+
#endif /* DEBUG */
47+
48+
#pragma mark - Legacy logging macros (deprecated - for backward compatibility during migration)
49+
// These will be removed after full migration to Android-style logging
50+
51+
#define DSLog(frmt, ...) DDLogInfo(frmt, ##__VA_ARGS__)
2952

3053
#ifdef DEBUG
3154
#define DSLogPrivate(s, ...) DDLogVerbose(s, ##__VA_ARGS__)
3255
#else
3356
#define DSLogPrivate(s, ...)
3457
#endif /* DEBUG */
3558

36-
NS_ASSUME_NONNULL_BEGIN
37-
3859
@interface DSLogger : NSObject
3960

4061
+ (instancetype)sharedInstance;
4162

4263
- (NSArray<NSURL *> *)logFiles;
4364

4465
/** @fn log:
45-
* @brief This method is identical to `DSLog` macro
66+
* @brief This method logs a message with default class name
4667
* @param message Final message to log
4768
*/
4869
+ (void)log:(NSString *)message;
4970

71+
/** @fn log:className:
72+
* @brief This method logs a message with specified class name
73+
* @param message Final message to log
74+
* @param className The class name to include in the log
75+
*/
76+
+ (void)log:(NSString *)message className:(NSString *)className;
77+
5078
- (instancetype)init NS_UNAVAILABLE;
5179
+ (instancetype)new NS_UNAVAILABLE;
5280

0 commit comments

Comments
 (0)