Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for v1.0-beta-09 #25

Merged
merged 37 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e03fac2
Finish migration of Android source set.
KotlinGeekDev Feb 11, 2025
1781757
Configure the default OkHttp engine with ping interval support.
KotlinGeekDev Feb 11, 2025
4a47c76
Expose function for making a single filter request.
KotlinGeekDev Feb 11, 2025
ba0983c
Use varargs for the different Filter builder functions.
KotlinGeekDev Feb 11, 2025
72eb9fe
Add Kind 1111(comment) to list of kinds. Update README to show new AP…
KotlinGeekDev Feb 11, 2025
14422c1
Remove Ktor-OkHttp engine ping config, until we find a reliable one.
KotlinGeekDev Feb 11, 2025
eb00315
NostrService refactor: Remove questionable and useless code. Move Eos…
KotlinGeekDev Feb 11, 2025
81f8712
Add custom Ktor HTTP client support. Update and make adjustments to R…
KotlinGeekDev Feb 11, 2025
ec4e442
Re-enable Maven publish plugin.
KotlinGeekDev Feb 11, 2025
14a0126
Add config for Jitpack CI builds.
KotlinGeekDev Feb 11, 2025
5a2ff43
Tests: Migrate to new APi definitions.
KotlinGeekDev Feb 11, 2025
43c2d7a
Disable publish at the module level.
KotlinGeekDev Feb 12, 2025
b418944
Rename project to Ballast. Refactor accordingly.
KotlinGeekDev Feb 12, 2025
93acacf
Move to vanniktech publish plugin. Add a publish release workflow.
KotlinGeekDev Feb 12, 2025
a6e2131
Enable Jitpack support and automate release publication.
KotlinGeekDev Feb 12, 2025
3855c30
Rename Tag properties to align with meaning in protocol specs.
KotlinGeekDev Feb 12, 2025
008137c
New release: v.10-beta-07
KotlinGeekDev Feb 12, 2025
f4fdb29
Update publish workflow.
KotlinGeekDev Feb 12, 2025
f39145d
Update publish workflow(again).
KotlinGeekDev Feb 12, 2025
103efac
Update README and disable build on push.
KotlinGeekDev Feb 12, 2025
ad7d4c5
Add shields badge for maven central.
KotlinGeekDev Feb 12, 2025
a879440
Clarify jitpack section in README.
KotlinGeekDev Feb 12, 2025
901e06a
Make slight adjustments to the customClient parameter.
KotlinGeekDev Feb 13, 2025
8b87d36
NIP-05 support, using the NostrUtils.getProfileInfoFromAddress() API,…
KotlinGeekDev Feb 13, 2025
4e17a62
Make adjustments and add convenience functions for relay pool managem…
KotlinGeekDev Feb 13, 2025
9bd1806
Add new API: clearRelayPool().
KotlinGeekDev Feb 13, 2025
98cbeb3
Use hex conversion for Uuid in singleRequestFilter, until Kotlin UUID…
KotlinGeekDev Feb 13, 2025
52d443a
Introduce new API: getMetadataFor(profile, relays). Fix requestFromRe…
KotlinGeekDev Feb 13, 2025
63e75a7
Move to new name: Rhodium
KotlinGeekDev Feb 13, 2025
ce0955f
Prepare new version: v1.0-beta-08. Adjust README to new version and n…
KotlinGeekDev Feb 14, 2025
5478108
Merge branch 'main' into develop
KotlinGeekDev Feb 14, 2025
9e87a99
Remove the file that should not have been.
KotlinGeekDev Feb 14, 2025
9b366ef
Re-enable build on push.
KotlinGeekDev Feb 14, 2025
8a8f1d2
Bech32 support(encode/decode, parsing). Adapt copied code to KMP.
KotlinGeekDev Feb 14, 2025
216aac7
Add a logging solution. Disable build on push.
KotlinGeekDev Feb 14, 2025
37ebb09
Prepare new release: v1.0-beta-09.
KotlinGeekDev Feb 14, 2025
b1e12a1
Merge branch 'main' into develop
KotlinGeekDev Feb 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ You can include the library from either Maven Central or Jitpack.
You can include the library in the common source set like this:
```kotlin
dependencies {
implementation("io.github.kotlingeekdev:rhodium:1.0-beta-08")
implementation("io.github.kotlingeekdev:rhodium:1.0-beta-09")

}
```

Expand Down Expand Up @@ -73,7 +74,8 @@ then, in your module's `build.gradle(.kts)`, you need to add:
// build.gradle.kts
dependencies {
//...
implementation("com.github.KotlinGeekDev.Rhodium:rhodium:1.0-beta-08")
implementation("com.github.KotlinGeekDev.Rhodium:rhodium:1.0-beta-09")


}

Expand All @@ -83,7 +85,7 @@ If you're including it in an Android app, you can just add:
// app/build.gradle.kts
dependencies {
//...
implementation("com.github.KotlinGeekDev.Rhodium:rhodium-android:1.0-beta-08")
implementation("com.github.KotlinGeekDev.Rhodium:rhodium-android:1.0-beta-09")

}
```
Expand Down
4 changes: 2 additions & 2 deletions ballast-core/src/commonMain/kotlin/ballast/nostr/EventExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fun Event.isValid(): Boolean {
this.tags, this.content
)
if (eventId != this.id) {
println("The event id is invalid.")
serviceLogger.w("The event id is invalid.")
return false
}
val signatureValidity = CryptoUtils.verifyContentSignature(
Expand All @@ -86,7 +86,7 @@ fun Event.isValid(): Boolean {
Hex.decode(eventId)
)
if (!signatureValidity) {
println("The event signature is invalid.\n Please check the pubkey, or content.")
serviceLogger.w("The event signature is invalid.\n Please check the pubkey, or content.")
return false
}
return true
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ allprojects {
val isJitpack = System.getenv("JITPACK") == "true"

group = "io.github.kotlingeekdev"
version = "1.0-beta-08"
version = "1.0-beta-09"


// val javadocJar = tasks.register<Jar>("javadocJar") {
Expand Down
4 changes: 4 additions & 0 deletions rhodium-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ kotlin {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
//UUID
implementation("com.benasher44:uuid:0.8.4")
//ByteBuffer(until a kotlinx-io replacement appears)
implementation("com.ditchoom:buffer:1.4.2")
//Logging
implementation("co.touchlab:kermit:2.0.5")
}

commonTest.dependencies {
Expand Down
308 changes: 308 additions & 0 deletions rhodium-core/src/commonMain/kotlin/rhodium/crypto/Bech32Util.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
/**
* Copyright (c) 2022 KotlinGeekDev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package rhodium.crypto

/**
* Copyright (c) 2024 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
///////////////////////////////////////////////////////////////////////////////////
/*
* Copyright 2020 ACINQ SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import kotlin.jvm.JvmStatic
/**
* Bech32 works with 5 bits values, we use this type to make it explicit: whenever you see Int5 it
* means 5 bits values, and whenever you see Byte it means 8 bits values.
*/
private typealias Int5 = Byte

/**
* Bech32 and Bech32m address formats. See
* https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki and
* https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki.
*/
object Bech32 {
const val ALPHABET: String = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
const val ALPHABET_UPPERCASE: String = "QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L"

enum class Encoding(
val constant: Int,
) {
Bech32(1),
Bech32m(0x2bc830a3),
Beck32WithoutChecksum(0),
}

// char -> 5 bits value
private val map = Array<Int5>(255) { -1 }

init {
for (i in 0..ALPHABET.lastIndex) {
map[ALPHABET[i].code] = i.toByte()
}
for (i in 0..ALPHABET_UPPERCASE.lastIndex) {
map[ALPHABET_UPPERCASE[i].code] = i.toByte()
}
}

fun expand(hrp: String): Array<Int5> {
val half = hrp.length + 1
val size = half + hrp.length
return Array<Int5>(size) {
when (it) {
in hrp.indices -> hrp[it].code.shr(5).toByte()
in half until size -> (hrp[it - half].code and 31).toByte()
else -> 0
}
}
}

private val GEN = arrayOf(0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3)

fun polymod(
values: Array<Int5>,
values1: Array<Int5>,
): Int {
var chk = 1
values.forEach { v ->
val b = chk shr 25
chk = ((chk and 0x1ffffff) shl 5) xor v.toInt()
for (i in 0..4) {
if (((b shr i) and 1) != 0) chk = chk xor GEN[i]
}
}
values1.forEach { v ->
val b = chk shr 25
chk = ((chk and 0x1ffffff) shl 5) xor v.toInt()
for (i in 0..4) {
if (((b shr i) and 1) != 0) chk = chk xor GEN[i]
}
}
return chk
}

/**
* @param hrp human readable prefix
* @param int5s 5-bit data
* @param encoding encoding to use (bech32 or bech32m)
* @return hrp + data encoded as a Bech32 string
*/
@JvmStatic
public fun encode(
hrp: String,
int5s: ArrayList<Int5>,
encoding: Encoding,
): String {
require(hrp.lowercase() == hrp || hrp.uppercase() == hrp) {
"mixed case strings are not valid bech32 prefixes"
}
val dataWithChecksum =
when (encoding) {
Encoding.Beck32WithoutChecksum -> int5s
else -> addChecksum(hrp, int5s, encoding)
}

val charArray =
CharArray(dataWithChecksum.size) { ALPHABET[dataWithChecksum[it].toInt()] }.concatToString()

return hrp + "1" + charArray
}

/**
* @param hrp human readable prefix
* @param data data to encode
* @param encoding encoding to use (bech32 or bech32m)
* @return hrp + data encoded as a Bech32 string
*/
@JvmStatic
public fun encodeBytes(
hrp: String,
data: ByteArray,
encoding: Encoding,
): String = encode(hrp, eight2five(data), encoding)

/**
* decodes a bech32 string
*
* @param bech32 bech32 string
* @param noChecksum if true, the bech32 string doesn't have a checksum
* @return a (hrp, data, encoding) tuple
*/
@JvmStatic
public fun decode(
bech32: String,
noChecksum: Boolean = false,
): Triple<String, Array<Int5>, Encoding> {
var pos = 0
bech32.forEachIndexed { index, char ->
require(char.code in 33..126) { "invalid character $char" }
if (char == '1') {
pos = index
}
}

val hrp = bech32.take(pos).lowercase() // strings must be lower case
require(hrp.length in 1..83) { "hrp must contain 1 to 83 characters" }

val data = Array(bech32.length - pos - 1) { map[bech32[pos + 1 + it].code] }

return if (noChecksum) {
Triple(hrp, data, Encoding.Beck32WithoutChecksum)
} else {
val encoding =
when (polymod(expand(hrp), data)) {
Encoding.Bech32.constant -> Encoding.Bech32
Encoding.Bech32m.constant -> Encoding.Bech32m
else -> throw IllegalArgumentException("invalid checksum for $bech32")
}
Triple(hrp, data.copyOfRange(0, data.size - 6), encoding)
}
}

/**
* decodes a bech32 string
*
* @param bech32 bech32 string
* @param noChecksum if true, the bech32 string doesn't have a checksum
* @return a (hrp, data, encoding) tuple
*/
@JvmStatic
public fun decodeBytes(
bech32: String,
noChecksum: Boolean = false,
): Triple<String, ByteArray, Encoding> {
val (hrp, int5s, encoding) = decode(bech32, noChecksum)
return Triple(hrp, five2eight(int5s, 0), encoding)
}

val ZEROS = arrayOf(0.toByte(), 0.toByte(), 0.toByte(), 0.toByte(), 0.toByte(), 0.toByte())

/**
* @param hrp Human Readable Part
* @param data data (a sequence of 5 bits integers)
* @param encoding encoding to use (bech32 or bech32m)
* @return a checksum computed over hrp and data
*/
private fun addChecksum(
hrp: String,
data: ArrayList<Int5>,
encoding: Encoding,
): ArrayList<Int5> {
val values = expand(hrp) + data
val poly = polymod(values, ZEROS) xor encoding.constant

for (i in 0 until 6) {
data.add((poly.shr(5 * (5 - i)) and 31).toByte())
}

return data
}

/**
* @param input a sequence of 8 bits integers
* @return a sequence of 5 bits integers
*/
@JvmStatic
public fun eight2five(input: ByteArray): ArrayList<Int5> {
var buffer = 0L
val output =
ArrayList<Int5>(input.size * 2) // larger array on purpose. Checksum is added later.
var count = 0
input.forEach { b ->
buffer = (buffer shl 8) or (b.toLong() and 0xff)
count += 8
while (count >= 5) {
output.add(((buffer shr (count - 5)) and 31).toByte())
count -= 5
}
}
if (count > 0) output.add(((buffer shl (5 - count)) and 31).toByte())
return output
}

/**
* @param input a sequence of 5 bits integers
* @return a sequence of 8 bits integers
*/
@JvmStatic
public fun five2eight(
input: Array<Int5>,
offset: Int,
): ByteArray {
var buffer = 0L
val output = ArrayList<Byte>(input.size)
var count = 0
for (i in offset..input.lastIndex) {
val b = input[i]
buffer = (buffer shl 5) or (b.toLong() and 31)
count += 5
while (count >= 8) {
output.add(((buffer shr (count - 8)) and 0xff).toByte())
count -= 8
}
}
require(count <= 4) { "Zero-padding of more than 4 bits" }
require((buffer and ((1L shl count) - 1L)) == 0L) { "Non-zero padding in 8-to-5 conversion" }
return output.toByteArray()
}
}

fun String.bechToBytes(hrp: String? = null): ByteArray {
val decodedForm = Bech32.decodeBytes(this)
hrp?.also {
if (it != decodedForm.first) {
throw IllegalArgumentException("Expected $it but obtained ${decodedForm.first}")
}
}
return decodedForm.second
}
Loading