diff --git a/Cargo.lock b/Cargo.lock index 1cab7a6..575214f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,7 +475,7 @@ dependencies = [ [[package]] name = "bitkitcore" -version = "0.1.57" +version = "0.1.58" dependencies = [ "android_logger", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 8019578..21ec542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bitkitcore" -version = "0.1.57" +version = "0.1.58" edition = "2021" [lib] diff --git a/Package.swift b/Package.swift index fcae1cd..4a1cf8b 100644 --- a/Package.swift +++ b/Package.swift @@ -3,8 +3,8 @@ import PackageDescription -let tag = "v0.1.57" -let checksum = "fe0b5fa50a9bad4330fc6fb74bcec335459188fdcf543a6b971af3356161bb85" +let tag = "v0.1.58" +let checksum = "89821971ec3f1ce8c16ac46ed5b35ddaf127e087113829b1a6b6cb023a449c88" let url = "https://github.com/synonymdev/bitkit-core/releases/download/\(tag)/BitkitCore.xcframework.zip" let package = Package( diff --git a/bindings/android/gradle.properties b/bindings/android/gradle.properties index 876e8ca..14e2677 100644 --- a/bindings/android/gradle.properties +++ b/bindings/android/gradle.properties @@ -3,4 +3,4 @@ android.useAndroidX=true android.enableJetifier=true kotlin.code.style=official group=com.synonym -version=0.1.57 +version=0.1.58 diff --git a/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so index 251bc2f..a8f2001 100755 Binary files a/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/arm64-v8a/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/arm64-v8a/libpubky_app_specs-90a3a8b6be1a7a22.so b/bindings/android/lib/src/main/jniLibs/arm64-v8a/libpubky_app_specs-90a3a8b6be1a7a22.so new file mode 100755 index 0000000..2856ba2 Binary files /dev/null and b/bindings/android/lib/src/main/jniLibs/arm64-v8a/libpubky_app_specs-90a3a8b6be1a7a22.so differ diff --git a/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so index 4bf93e9..116d9d9 100755 Binary files a/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libpubky_app_specs-a5b33175be180155.so b/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libpubky_app_specs-a5b33175be180155.so new file mode 100755 index 0000000..9df27c8 Binary files /dev/null and b/bindings/android/lib/src/main/jniLibs/armeabi-v7a/libpubky_app_specs-a5b33175be180155.so differ diff --git a/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so index 24a338c..f5f78d6 100755 Binary files a/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/x86/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/x86/libpubky_app_specs-6691cf4f97b9d92f.so b/bindings/android/lib/src/main/jniLibs/x86/libpubky_app_specs-6691cf4f97b9d92f.so new file mode 100755 index 0000000..d08e98c Binary files /dev/null and b/bindings/android/lib/src/main/jniLibs/x86/libpubky_app_specs-6691cf4f97b9d92f.so differ diff --git a/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so b/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so index 98f9f1d..93d425e 100755 Binary files a/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so and b/bindings/android/lib/src/main/jniLibs/x86_64/libbitkitcore.so differ diff --git a/bindings/android/lib/src/main/jniLibs/x86_64/libpubky_app_specs-4ad86c877f377f40.so b/bindings/android/lib/src/main/jniLibs/x86_64/libpubky_app_specs-4ad86c877f377f40.so new file mode 100755 index 0000000..8f8f793 Binary files /dev/null and b/bindings/android/lib/src/main/jniLibs/x86_64/libpubky_app_specs-4ad86c877f377f40.so differ diff --git a/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt b/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt index a08952b..cb8bd74 100644 --- a/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt +++ b/bindings/android/lib/src/main/kotlin/com/synonym/bitkitcore/bitkitcore.android.kt @@ -6220,6 +6220,7 @@ public object FfiConverterTypeLightningActivity: FfiConverterRustBufferHeadersPath Headers LibraryIdentifier - ios-arm64-simulator + ios-arm64 LibraryPath libbitkitcore.a SupportedArchitectures @@ -19,8 +19,6 @@ SupportedPlatform ios - SupportedPlatformVariant - simulator BinaryPath @@ -28,7 +26,7 @@ HeadersPath Headers LibraryIdentifier - ios-arm64 + ios-arm64-simulator LibraryPath libbitkitcore.a SupportedArchitectures @@ -37,6 +35,8 @@ SupportedPlatform ios + SupportedPlatformVariant + simulator CFBundlePackageType diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a index 210e991..ed4f18d 100644 Binary files a/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a and b/bindings/ios/BitkitCore.xcframework/ios-arm64-simulator/libbitkitcore.a differ diff --git a/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a b/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a index 901de65..0134942 100644 Binary files a/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a and b/bindings/ios/BitkitCore.xcframework/ios-arm64/libbitkitcore.a differ diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index 3c30aac..000a65e 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -6645,13 +6645,14 @@ public struct LightningActivity { public var message: String public var timestamp: UInt64 public var preimage: String? + public var contact: String? public var createdAt: UInt64? public var updatedAt: UInt64? public var seenAt: UInt64? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, txType: PaymentType, status: PaymentState, value: UInt64, fee: UInt64?, invoice: String, message: String, timestamp: UInt64, preimage: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { + public init(id: String, txType: PaymentType, status: PaymentState, value: UInt64, fee: UInt64?, invoice: String, message: String, timestamp: UInt64, preimage: String?, contact: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { self.id = id self.txType = txType self.status = status @@ -6661,6 +6662,7 @@ public struct LightningActivity { self.message = message self.timestamp = timestamp self.preimage = preimage + self.contact = contact self.createdAt = createdAt self.updatedAt = updatedAt self.seenAt = seenAt @@ -6701,6 +6703,9 @@ extension LightningActivity: Equatable, Hashable { if lhs.preimage != rhs.preimage { return false } + if lhs.contact != rhs.contact { + return false + } if lhs.createdAt != rhs.createdAt { return false } @@ -6723,6 +6728,7 @@ extension LightningActivity: Equatable, Hashable { hasher.combine(message) hasher.combine(timestamp) hasher.combine(preimage) + hasher.combine(contact) hasher.combine(createdAt) hasher.combine(updatedAt) hasher.combine(seenAt) @@ -6749,6 +6755,7 @@ public struct FfiConverterTypeLightningActivity: FfiConverterRustBuffer { message: FfiConverterString.read(from: &buf), timestamp: FfiConverterUInt64.read(from: &buf), preimage: FfiConverterOptionString.read(from: &buf), + contact: FfiConverterOptionString.read(from: &buf), createdAt: FfiConverterOptionUInt64.read(from: &buf), updatedAt: FfiConverterOptionUInt64.read(from: &buf), seenAt: FfiConverterOptionUInt64.read(from: &buf) @@ -6765,6 +6772,7 @@ public struct FfiConverterTypeLightningActivity: FfiConverterRustBuffer { FfiConverterString.write(value.message, into: &buf) FfiConverterUInt64.write(value.timestamp, into: &buf) FfiConverterOptionString.write(value.preimage, into: &buf) + FfiConverterOptionString.write(value.contact, into: &buf) FfiConverterOptionUInt64.write(value.createdAt, into: &buf) FfiConverterOptionUInt64.write(value.updatedAt, into: &buf) FfiConverterOptionUInt64.write(value.seenAt, into: &buf) @@ -7645,13 +7653,14 @@ public struct OnchainActivity { public var confirmTimestamp: UInt64? public var channelId: String? public var transferTxId: String? + public var contact: String? public var createdAt: UInt64? public var updatedAt: UInt64? public var seenAt: UInt64? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(id: String, txType: PaymentType, txId: String, value: UInt64, fee: UInt64, feeRate: UInt64, address: String, confirmed: Bool, timestamp: UInt64, isBoosted: Bool, boostTxIds: [String], isTransfer: Bool, doesExist: Bool, confirmTimestamp: UInt64?, channelId: String?, transferTxId: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { + public init(id: String, txType: PaymentType, txId: String, value: UInt64, fee: UInt64, feeRate: UInt64, address: String, confirmed: Bool, timestamp: UInt64, isBoosted: Bool, boostTxIds: [String], isTransfer: Bool, doesExist: Bool, confirmTimestamp: UInt64?, channelId: String?, transferTxId: String?, contact: String?, createdAt: UInt64?, updatedAt: UInt64?, seenAt: UInt64?) { self.id = id self.txType = txType self.txId = txId @@ -7668,6 +7677,7 @@ public struct OnchainActivity { self.confirmTimestamp = confirmTimestamp self.channelId = channelId self.transferTxId = transferTxId + self.contact = contact self.createdAt = createdAt self.updatedAt = updatedAt self.seenAt = seenAt @@ -7729,6 +7739,9 @@ extension OnchainActivity: Equatable, Hashable { if lhs.transferTxId != rhs.transferTxId { return false } + if lhs.contact != rhs.contact { + return false + } if lhs.createdAt != rhs.createdAt { return false } @@ -7758,6 +7771,7 @@ extension OnchainActivity: Equatable, Hashable { hasher.combine(confirmTimestamp) hasher.combine(channelId) hasher.combine(transferTxId) + hasher.combine(contact) hasher.combine(createdAt) hasher.combine(updatedAt) hasher.combine(seenAt) @@ -7791,6 +7805,7 @@ public struct FfiConverterTypeOnchainActivity: FfiConverterRustBuffer { confirmTimestamp: FfiConverterOptionUInt64.read(from: &buf), channelId: FfiConverterOptionString.read(from: &buf), transferTxId: FfiConverterOptionString.read(from: &buf), + contact: FfiConverterOptionString.read(from: &buf), createdAt: FfiConverterOptionUInt64.read(from: &buf), updatedAt: FfiConverterOptionUInt64.read(from: &buf), seenAt: FfiConverterOptionUInt64.read(from: &buf) @@ -7814,6 +7829,7 @@ public struct FfiConverterTypeOnchainActivity: FfiConverterRustBuffer { FfiConverterOptionUInt64.write(value.confirmTimestamp, into: &buf) FfiConverterOptionString.write(value.channelId, into: &buf) FfiConverterOptionString.write(value.transferTxId, into: &buf) + FfiConverterOptionString.write(value.contact, into: &buf) FfiConverterOptionUInt64.write(value.createdAt, into: &buf) FfiConverterOptionUInt64.write(value.updatedAt, into: &buf) FfiConverterOptionUInt64.write(value.seenAt, into: &buf) diff --git a/bindings/python/bitkitcore/bitkitcore.py b/bindings/python/bitkitcore/bitkitcore.py index 2ad1664..a1d05f6 100644 --- a/bindings/python/bitkitcore/bitkitcore.py +++ b/bindings/python/bitkitcore/bitkitcore.py @@ -5852,10 +5852,11 @@ class LightningActivity: message: "str" timestamp: "int" preimage: "typing.Optional[str]" + contact: "typing.Optional[str]" created_at: "typing.Optional[int]" updated_at: "typing.Optional[int]" seen_at: "typing.Optional[int]" - def __init__(self, *, id: "str", tx_type: "PaymentType", status: "PaymentState", value: "int", fee: "typing.Optional[int]", invoice: "str", message: "str", timestamp: "int", preimage: "typing.Optional[str]", created_at: "typing.Optional[int]", updated_at: "typing.Optional[int]", seen_at: "typing.Optional[int]"): + def __init__(self, *, id: "str", tx_type: "PaymentType", status: "PaymentState", value: "int", fee: "typing.Optional[int]", invoice: "str", message: "str", timestamp: "int", preimage: "typing.Optional[str]", contact: "typing.Optional[str]", created_at: "typing.Optional[int]", updated_at: "typing.Optional[int]", seen_at: "typing.Optional[int]"): self.id = id self.tx_type = tx_type self.status = status @@ -5865,12 +5866,13 @@ def __init__(self, *, id: "str", tx_type: "PaymentType", status: "PaymentState", self.message = message self.timestamp = timestamp self.preimage = preimage + self.contact = contact self.created_at = created_at self.updated_at = updated_at self.seen_at = seen_at def __str__(self): - return "LightningActivity(id={}, tx_type={}, status={}, value={}, fee={}, invoice={}, message={}, timestamp={}, preimage={}, created_at={}, updated_at={}, seen_at={})".format(self.id, self.tx_type, self.status, self.value, self.fee, self.invoice, self.message, self.timestamp, self.preimage, self.created_at, self.updated_at, self.seen_at) + return "LightningActivity(id={}, tx_type={}, status={}, value={}, fee={}, invoice={}, message={}, timestamp={}, preimage={}, contact={}, created_at={}, updated_at={}, seen_at={})".format(self.id, self.tx_type, self.status, self.value, self.fee, self.invoice, self.message, self.timestamp, self.preimage, self.contact, self.created_at, self.updated_at, self.seen_at) def __eq__(self, other): if self.id != other.id: @@ -5891,6 +5893,8 @@ def __eq__(self, other): return False if self.preimage != other.preimage: return False + if self.contact != other.contact: + return False if self.created_at != other.created_at: return False if self.updated_at != other.updated_at: @@ -5912,6 +5916,7 @@ def read(buf): message=_UniffiConverterString.read(buf), timestamp=_UniffiConverterUInt64.read(buf), preimage=_UniffiConverterOptionalString.read(buf), + contact=_UniffiConverterOptionalString.read(buf), created_at=_UniffiConverterOptionalUInt64.read(buf), updated_at=_UniffiConverterOptionalUInt64.read(buf), seen_at=_UniffiConverterOptionalUInt64.read(buf), @@ -5928,6 +5933,7 @@ def check_lower(value): _UniffiConverterString.check_lower(value.message) _UniffiConverterUInt64.check_lower(value.timestamp) _UniffiConverterOptionalString.check_lower(value.preimage) + _UniffiConverterOptionalString.check_lower(value.contact) _UniffiConverterOptionalUInt64.check_lower(value.created_at) _UniffiConverterOptionalUInt64.check_lower(value.updated_at) _UniffiConverterOptionalUInt64.check_lower(value.seen_at) @@ -5943,6 +5949,7 @@ def write(value, buf): _UniffiConverterString.write(value.message, buf) _UniffiConverterUInt64.write(value.timestamp, buf) _UniffiConverterOptionalString.write(value.preimage, buf) + _UniffiConverterOptionalString.write(value.contact, buf) _UniffiConverterOptionalUInt64.write(value.created_at, buf) _UniffiConverterOptionalUInt64.write(value.updated_at, buf) _UniffiConverterOptionalUInt64.write(value.seen_at, buf) @@ -6480,10 +6487,11 @@ class OnchainActivity: confirm_timestamp: "typing.Optional[int]" channel_id: "typing.Optional[str]" transfer_tx_id: "typing.Optional[str]" + contact: "typing.Optional[str]" created_at: "typing.Optional[int]" updated_at: "typing.Optional[int]" seen_at: "typing.Optional[int]" - def __init__(self, *, id: "str", tx_type: "PaymentType", tx_id: "str", value: "int", fee: "int", fee_rate: "int", address: "str", confirmed: "bool", timestamp: "int", is_boosted: "bool", boost_tx_ids: "typing.List[str]", is_transfer: "bool", does_exist: "bool", confirm_timestamp: "typing.Optional[int]", channel_id: "typing.Optional[str]", transfer_tx_id: "typing.Optional[str]", created_at: "typing.Optional[int]", updated_at: "typing.Optional[int]", seen_at: "typing.Optional[int]"): + def __init__(self, *, id: "str", tx_type: "PaymentType", tx_id: "str", value: "int", fee: "int", fee_rate: "int", address: "str", confirmed: "bool", timestamp: "int", is_boosted: "bool", boost_tx_ids: "typing.List[str]", is_transfer: "bool", does_exist: "bool", confirm_timestamp: "typing.Optional[int]", channel_id: "typing.Optional[str]", transfer_tx_id: "typing.Optional[str]", contact: "typing.Optional[str]", created_at: "typing.Optional[int]", updated_at: "typing.Optional[int]", seen_at: "typing.Optional[int]"): self.id = id self.tx_type = tx_type self.tx_id = tx_id @@ -6500,12 +6508,13 @@ def __init__(self, *, id: "str", tx_type: "PaymentType", tx_id: "str", value: "i self.confirm_timestamp = confirm_timestamp self.channel_id = channel_id self.transfer_tx_id = transfer_tx_id + self.contact = contact self.created_at = created_at self.updated_at = updated_at self.seen_at = seen_at def __str__(self): - return "OnchainActivity(id={}, tx_type={}, tx_id={}, value={}, fee={}, fee_rate={}, address={}, confirmed={}, timestamp={}, is_boosted={}, boost_tx_ids={}, is_transfer={}, does_exist={}, confirm_timestamp={}, channel_id={}, transfer_tx_id={}, created_at={}, updated_at={}, seen_at={})".format(self.id, self.tx_type, self.tx_id, self.value, self.fee, self.fee_rate, self.address, self.confirmed, self.timestamp, self.is_boosted, self.boost_tx_ids, self.is_transfer, self.does_exist, self.confirm_timestamp, self.channel_id, self.transfer_tx_id, self.created_at, self.updated_at, self.seen_at) + return "OnchainActivity(id={}, tx_type={}, tx_id={}, value={}, fee={}, fee_rate={}, address={}, confirmed={}, timestamp={}, is_boosted={}, boost_tx_ids={}, is_transfer={}, does_exist={}, confirm_timestamp={}, channel_id={}, transfer_tx_id={}, contact={}, created_at={}, updated_at={}, seen_at={})".format(self.id, self.tx_type, self.tx_id, self.value, self.fee, self.fee_rate, self.address, self.confirmed, self.timestamp, self.is_boosted, self.boost_tx_ids, self.is_transfer, self.does_exist, self.confirm_timestamp, self.channel_id, self.transfer_tx_id, self.contact, self.created_at, self.updated_at, self.seen_at) def __eq__(self, other): if self.id != other.id: @@ -6540,6 +6549,8 @@ def __eq__(self, other): return False if self.transfer_tx_id != other.transfer_tx_id: return False + if self.contact != other.contact: + return False if self.created_at != other.created_at: return False if self.updated_at != other.updated_at: @@ -6568,6 +6579,7 @@ def read(buf): confirm_timestamp=_UniffiConverterOptionalUInt64.read(buf), channel_id=_UniffiConverterOptionalString.read(buf), transfer_tx_id=_UniffiConverterOptionalString.read(buf), + contact=_UniffiConverterOptionalString.read(buf), created_at=_UniffiConverterOptionalUInt64.read(buf), updated_at=_UniffiConverterOptionalUInt64.read(buf), seen_at=_UniffiConverterOptionalUInt64.read(buf), @@ -6591,6 +6603,7 @@ def check_lower(value): _UniffiConverterOptionalUInt64.check_lower(value.confirm_timestamp) _UniffiConverterOptionalString.check_lower(value.channel_id) _UniffiConverterOptionalString.check_lower(value.transfer_tx_id) + _UniffiConverterOptionalString.check_lower(value.contact) _UniffiConverterOptionalUInt64.check_lower(value.created_at) _UniffiConverterOptionalUInt64.check_lower(value.updated_at) _UniffiConverterOptionalUInt64.check_lower(value.seen_at) @@ -6613,6 +6626,7 @@ def write(value, buf): _UniffiConverterOptionalUInt64.write(value.confirm_timestamp, buf) _UniffiConverterOptionalString.write(value.channel_id, buf) _UniffiConverterOptionalString.write(value.transfer_tx_id, buf) + _UniffiConverterOptionalString.write(value.contact, buf) _UniffiConverterOptionalUInt64.write(value.created_at, buf) _UniffiConverterOptionalUInt64.write(value.updated_at, buf) _UniffiConverterOptionalUInt64.write(value.seen_at, buf) diff --git a/bindings/python/bitkitcore/libbitkitcore.dylib b/bindings/python/bitkitcore/libbitkitcore.dylib index 9a0cd92..d7a5bfd 100755 Binary files a/bindings/python/bitkitcore/libbitkitcore.dylib and b/bindings/python/bitkitcore/libbitkitcore.dylib differ diff --git a/src/modules/activity/implementation.rs b/src/modules/activity/implementation.rs index 31a9d32..8174f0e 100644 --- a/src/modules/activity/implementation.rs +++ b/src/modules/activity/implementation.rs @@ -17,7 +17,8 @@ const CREATE_ACTIVITIES_TABLE: &str = " timestamp INTEGER NOT NULL CHECK (timestamp > 0), created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), - seen_at INTEGER CHECK (seen_at IS NULL OR seen_at > 0) + seen_at INTEGER CHECK (seen_at IS NULL OR seen_at > 0), + contact TEXT CHECK (contact IS NULL OR length(contact) > 0) )"; const CREATE_ONCHAIN_TABLE: &str = " @@ -184,10 +185,16 @@ const TRIGGER_STATEMENTS: &[&str] = &[ /// Each entry is (column_name, ALTER TABLE statement). The column is checked /// via `PRAGMA table_info` before running the statement to avoid relying on /// locale-dependent SQLite error messages. -const MIGRATIONS: &[(&str, &str)] = &[( - "seen_at", - "ALTER TABLE activities ADD COLUMN seen_at INTEGER CHECK (seen_at IS NULL OR seen_at > 0)", -)]; +const MIGRATIONS: &[(&str, &str)] = &[ + ( + "seen_at", + "ALTER TABLE activities ADD COLUMN seen_at INTEGER CHECK (seen_at IS NULL OR seen_at > 0)", + ), + ( + "contact", + "ALTER TABLE activities ADD COLUMN contact TEXT CHECK (contact IS NULL OR length(contact) > 0)", + ), +]; impl ActivityDB { /// Creates a new ActivityDB instance with the specified database path. @@ -368,9 +375,9 @@ impl ActivityDB { let activities_sql = " INSERT INTO activities ( - id, activity_type, tx_type, timestamp + id, activity_type, tx_type, timestamp, contact ) VALUES ( - ?1, 'onchain', ?2, ?3 + ?1, 'onchain', ?2, ?3, ?4 )"; tx.execute( @@ -379,6 +386,7 @@ impl ActivityDB { &activity.id, Self::payment_type_to_string(&activity.tx_type), activity.timestamp, + &activity.contact, ), ) .map_err(|e| ActivityError::InsertError { @@ -454,9 +462,9 @@ impl ActivityDB { let activities_sql = " INSERT INTO activities ( - id, activity_type, tx_type, timestamp + id, activity_type, tx_type, timestamp, contact ) VALUES ( - ?1, 'lightning', ?2, ?3 + ?1, 'lightning', ?2, ?3, ?4 )"; tx.execute( @@ -465,6 +473,7 @@ impl ActivityDB { &activity.id, Self::payment_type_to_string(&activity.tx_type), activity.timestamp, + &activity.contact, ), ) .map_err(|e| ActivityError::InsertError { @@ -520,7 +529,7 @@ impl ActivityDB { { let mut stmt_act = tx.prepare( - "INSERT OR REPLACE INTO activities (id, activity_type, tx_type, timestamp) VALUES (?1, 'onchain', ?2, ?3)" + "INSERT OR REPLACE INTO activities (id, activity_type, tx_type, timestamp, contact) VALUES (?1, 'onchain', ?2, ?3, ?4)" ).map_err(|e| ActivityError::DataError { error_details: format!("Failed to prepare activities statement: {}", e), })?; @@ -550,6 +559,7 @@ impl ActivityDB { &activity.id, Self::payment_type_to_string(&activity.tx_type), activity.timestamp, + &activity.contact, )) .map_err(|e| ActivityError::InsertError { error_details: format!("Failed to upsert activities: {}", e), @@ -603,7 +613,7 @@ impl ActivityDB { { let mut stmt_act = tx.prepare( - "INSERT OR REPLACE INTO activities (id, activity_type, tx_type, timestamp) VALUES (?1, 'lightning', ?2, ?3)" + "INSERT OR REPLACE INTO activities (id, activity_type, tx_type, timestamp, contact) VALUES (?1, 'lightning', ?2, ?3, ?4)" ).map_err(|e| ActivityError::DataError { error_details: format!("Failed to prepare activities statement: {}", e), })?; @@ -631,6 +641,7 @@ impl ActivityDB { &activity.id, Self::payment_type_to_string(&activity.tx_type), activity.timestamp, + &activity.contact, )) .map_err(|e| ActivityError::InsertError { error_details: format!("Failed to upsert activities: {}", e), @@ -728,10 +739,11 @@ impl ActivityDB { query.push_str(&format!( " AND ( o.address LIKE '{}' OR + a.contact LIKE '{}' OR l.invoice LIKE '{}' OR l.message LIKE '{}' )", - search_pattern, search_pattern, search_pattern + search_pattern, search_pattern, search_pattern, search_pattern )); } } @@ -771,7 +783,8 @@ impl ActivityDB { l.status AS ln_status, l.fee AS ln_fee, l.message AS ln_message, - l.preimage AS ln_preimage + l.preimage AS ln_preimage, + a.contact AS contact FROM activities a INNER JOIN filtered_activities fa ON a.id = fa.id @@ -833,6 +846,7 @@ impl ActivityDB { confirm_timestamp: confirm_timestamp.map(|t| t as u64), channel_id: row.get(18)?, transfer_tx_id: row.get(19)?, + contact: row.get(26)?, })) } "lightning" => { @@ -856,6 +870,7 @@ impl ActivityDB { fee: fee.map(|f| f as u64), message: row.get(24)?, preimage: row.get(25)?, + contact: row.get(26)?, })) } _ => Err(rusqlite::Error::InvalidColumnType( @@ -903,7 +918,8 @@ impl ActivityDB { a.id, a.tx_type, o.tx_id, o.value, o.fee, o.fee_rate, o.address, o.confirmed, a.timestamp, o.is_boosted, o.boost_tx_ids, o.is_transfer, o.does_exist, o.confirm_timestamp, - o.channel_id, o.transfer_tx_id, a.created_at, a.updated_at, a.seen_at + o.channel_id, o.transfer_tx_id, a.created_at, a.updated_at, a.seen_at, + a.contact FROM activities a JOIN onchain_activity o ON a.id = o.id WHERE a.id = ?1"; @@ -948,6 +964,7 @@ impl ActivityDB { confirm_timestamp: confirm_timestamp.map(|t| t as u64), channel_id: row.get(14)?, transfer_tx_id: row.get(15)?, + contact: row.get(19)?, created_at: created_at.map(|t| t as u64), updated_at: updated_at.map(|t| t as u64), seen_at: seen_at.map(|t| t as u64), @@ -966,7 +983,8 @@ impl ActivityDB { SELECT a.id, a.tx_type, l.status, l.value, l.fee, l.invoice, l.message, a.timestamp, - l.preimage, a.created_at, a.updated_at, a.seen_at + l.preimage, a.created_at, a.updated_at, a.seen_at, + a.contact FROM activities a JOIN lightning_activity l ON a.id = l.id WHERE a.id = ?1"; @@ -997,6 +1015,7 @@ impl ActivityDB { message: row.get(6)?, timestamp: timestamp as u64, preimage: row.get(8)?, + contact: row.get(12)?, created_at: created_at.map(|t| t as u64), updated_at: updated_at.map(|t| t as u64), seen_at: seen_at.map(|t| t as u64), @@ -1022,7 +1041,8 @@ impl ActivityDB { a.id, a.tx_type, o.tx_id, o.value, o.fee, o.fee_rate, o.address, o.confirmed, a.timestamp, o.is_boosted, o.boost_tx_ids, o.is_transfer, o.does_exist, o.confirm_timestamp, - o.channel_id, o.transfer_tx_id, a.created_at, a.updated_at, a.seen_at + o.channel_id, o.transfer_tx_id, a.created_at, a.updated_at, a.seen_at, + a.contact FROM activities a JOIN onchain_activity o ON a.id = o.id WHERE o.tx_id = ?1 AND a.activity_type = 'onchain' @@ -1068,6 +1088,7 @@ impl ActivityDB { confirm_timestamp: confirm_timestamp.map(|t| t as u64), channel_id: row.get(14)?, transfer_tx_id: row.get(15)?, + contact: row.get(19)?, created_at: created_at.map(|t| t as u64), updated_at: updated_at.map(|t| t as u64), seen_at: seen_at.map(|t| t as u64), @@ -1099,8 +1120,9 @@ impl ActivityDB { let activities_sql = " UPDATE activities SET tx_type = ?1, - timestamp = ?2 - WHERE id = ?3 AND activity_type = 'onchain'"; + timestamp = ?2, + contact = ?3 + WHERE id = ?4 AND activity_type = 'onchain'"; let rows = tx .execute( @@ -1108,6 +1130,7 @@ impl ActivityDB { ( Self::payment_type_to_string(&activity.tx_type), activity.timestamp, + &activity.contact, activity_id, ), ) @@ -1186,8 +1209,9 @@ impl ActivityDB { let activities_sql = " UPDATE activities SET tx_type = ?1, - timestamp = ?2 - WHERE id = ?3 AND activity_type = 'lightning'"; + timestamp = ?2, + contact = ?3 + WHERE id = ?4 AND activity_type = 'lightning'"; let rows = tx .execute( @@ -1195,6 +1219,7 @@ impl ActivityDB { ( Self::payment_type_to_string(&activity.tx_type), activity.timestamp, + &activity.contact, activity_id, ), ) diff --git a/src/modules/activity/tests.rs b/src/modules/activity/tests.rs index 9903e87..9273884 100644 --- a/src/modules/activity/tests.rs +++ b/src/modules/activity/tests.rs @@ -36,6 +36,7 @@ mod tests { confirm_timestamp: Some(1234568890), channel_id: None, transfer_tx_id: None, + contact: None, created_at: None, updated_at: None, seen_at: None, @@ -53,6 +54,7 @@ mod tests { message: "Test payment".to_string(), timestamp: 1234567890, preimage: Some("preimage123".to_string()), + contact: None, created_at: None, updated_at: None, seen_at: None, @@ -107,6 +109,40 @@ mod tests { cleanup(&db_path); } + #[test] + fn test_activity_migrations_add_contact_column() { + let db_path = format!("test_db_{}.sqlite", random::()); + { + let conn = rusqlite::Connection::open(&db_path).unwrap(); + conn.execute( + " + CREATE TABLE activities ( + id TEXT PRIMARY KEY, + activity_type TEXT NOT NULL CHECK (activity_type IN ('onchain', 'lightning')), + tx_type TEXT NOT NULL CHECK (tx_type IN ('sent', 'received')), + timestamp INTEGER NOT NULL CHECK (timestamp > 0), + created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), + updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) + )", + [], + ) + .unwrap(); + } + + let db = ActivityDB::new(&db_path).unwrap(); + let mut stmt = db.conn.prepare("PRAGMA table_info(activities)").unwrap(); + let columns = stmt + .query_map([], |row| row.get::<_, String>(1)) + .unwrap() + .collect::, _>>() + .unwrap(); + + assert!(columns.contains(&"seen_at".to_string())); + assert!(columns.contains(&"contact".to_string())); + + cleanup(&db_path); + } + #[test] fn test_insert_and_retrieve_onchain_activity() { let (mut db, db_path) = setup(); @@ -171,6 +207,92 @@ mod tests { cleanup(&db_path); } + #[test] + fn test_contact_preserved_for_activity_variants() { + let (mut db, db_path) = setup(); + let mut onchain = create_test_onchain_activity(); + let mut lightning = create_test_lightning_activity(); + + onchain.contact = Some("onchain_contact_pubky".to_string()); + lightning.contact = Some("lightning_contact_pubky".to_string()); + + db.insert_onchain_activity(&onchain).unwrap(); + db.insert_lightning_activity(&lightning).unwrap(); + + let onchain_by_id = db.get_activity_by_id(&onchain.id).unwrap().unwrap(); + match onchain_by_id { + Activity::Onchain(activity) => { + assert_eq!(activity.contact, Some("onchain_contact_pubky".to_string())); + } + Activity::Lightning(_) => panic!("Expected Onchain activity"), + } + + let onchain_by_tx_id = db.get_activity_by_tx_id(&onchain.tx_id).unwrap().unwrap(); + assert_eq!( + onchain_by_tx_id.contact, + Some("onchain_contact_pubky".to_string()) + ); + + let activities = db + .get_activities( + Some(ActivityFilter::All), + None, + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + + assert!(activities.iter().any(|activity| { + matches!(activity, Activity::Onchain(a) if a.contact.as_deref() == Some("onchain_contact_pubky")) + })); + assert!(activities.iter().any(|activity| { + matches!(activity, Activity::Lightning(a) if a.contact.as_deref() == Some("lightning_contact_pubky")) + })); + + cleanup(&db_path); + } + + #[test] + fn test_contact_updates_and_searches() { + let (mut db, db_path) = setup(); + let mut activity = create_test_lightning_activity(); + + db.insert_lightning_activity(&activity).unwrap(); + activity.contact = Some("searchable_contact_pubky".to_string()); + db.update_lightning_activity_by_id(&activity.id, &activity) + .unwrap(); + + let results = db + .get_activities( + Some(ActivityFilter::All), + None, + None, + Some("searchable_contact".to_string()), + None, + None, + None, + None, + ) + .unwrap(); + + assert_eq!(results.len(), 1); + match &results[0] { + Activity::Lightning(retrieved) => { + assert_eq!( + retrieved.contact, + Some("searchable_contact_pubky".to_string()) + ); + } + Activity::Onchain(_) => panic!("Expected Lightning activity"), + } + + cleanup(&db_path); + } + #[test] fn test_get_all_activities() { let (mut db, db_path) = setup(); diff --git a/src/modules/activity/types.rs b/src/modules/activity/types.rs index a1cc9ef..2d427fb 100644 --- a/src/modules/activity/types.rs +++ b/src/modules/activity/types.rs @@ -100,6 +100,8 @@ pub struct OnchainActivity { pub channel_id: Option, pub transfer_tx_id: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub contact: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub created_at: Option, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option, @@ -119,6 +121,8 @@ pub struct LightningActivity { pub timestamp: u64, pub preimage: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub contact: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub created_at: Option, #[serde(skip_serializing_if = "Option::is_none")] pub updated_at: Option,