diff --git a/CHANGELOG.md b/CHANGELOG.md index a84caa1..1bc71af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unrelease +## [0.6.4] - 2021-07-16 +### Changed +- Default Core API endpoint (https://stacks-node-api.stacks.co) + ## [0.6.3] - 2021-07-01 ### Added - ability to generate Stacks Addresses diff --git a/blockstack-sdk/build.gradle b/blockstack-sdk/build.gradle index a01cb76..223ca2b 100644 --- a/blockstack-sdk/build.gradle +++ b/blockstack-sdk/build.gradle @@ -12,7 +12,7 @@ android { minSdkVersion 21 targetSdkVersion 30 versionCode 2 - versionName "0.6.3" + versionName "0.6.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Blockstack.kt b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Blockstack.kt index e267b78..1f8eba1 100644 --- a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Blockstack.kt +++ b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Blockstack.kt @@ -22,6 +22,7 @@ import okhttp3.Request import okhttp3.Response import org.blockstack.android.sdk.extensions.toBtcAddress import org.blockstack.android.sdk.extensions.toHexPublicKey64 +import org.blockstack.android.sdk.extensions.toStxAddress import org.blockstack.android.sdk.model.* import org.json.JSONArray import org.json.JSONException @@ -99,7 +100,7 @@ class Blockstack(private val callFactory: Call.Factory = OkHttpClient(), }, "profile_url" to null, "hubUrl" to "https://hub.blockstack.org", - "blockstackAPIUrl" to "https://core.blockstack.org", + "blockstackAPIUrl" to DEFAULT_CORE_API_ENDPOINT, "associationToken" to null, "version" to VERSION ) @@ -296,8 +297,21 @@ class Blockstack(private val callFactory: Call.Factory = OkHttpClient(), val body = response.body!!.string() val nameInfo = JSONObject(body) val nameOwningAddress = nameInfo.optString("address") - val addressFromIssuer = DIDs.getAddressFromDID(payload.optString("iss")) - return nameOwningAddress.isNotEmpty() && nameOwningAddress == addressFromIssuer + val addressFromIssuer = DIDs.getAddressFromDID(payload.optString("iss")) ?: "" + + //Check if the address is a stx address + return if (nameOwningAddress.startsWith("S")) { + if (nameOwningAddress.isNotEmpty() && nameOwningAddress == addressFromIssuer) { + true + } else { + // Backward Compatibility (Address STX with BTC issuer) + // if the address is not the same, check if the profile belongs to the owner + nameInfo.optString("zonefile").contains(addressFromIssuer) + } + } else { + // legacy + nameOwningAddress.isNotEmpty() && nameOwningAddress == addressFromIssuer + } } else { return false } @@ -519,20 +533,16 @@ class Blockstack(private val callFactory: Call.Factory = OkHttpClient(), } val issuerPublicKey = payload.getJSONObject("issuer").getString("publicKey") - val uncompressedAddress = issuerPublicKey.toBtcAddress() + val uncompressedBtcAddress = issuerPublicKey.toBtcAddress() + val uncompressedStxAddress = issuerPublicKey.toStxAddress(true) if (publicKeyOrAddress == issuerPublicKey) { // pass - } else { - if (publicKeyOrAddress == uncompressedAddress) { - // pass - } else { - throw Error("Token issuer public key does not match the verifying value") - } + } else if (publicKeyOrAddress != uncompressedBtcAddress && publicKeyOrAddress != uncompressedStxAddress) { + throw Error("Token issuer public key does not match the verifying value") } return ProfileToken(tokenTripleToJSON(decodedToken)) - } private fun tokenTripleToJSON(decodedToken: Triple): JSONObject { diff --git a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/BlockstackSession.kt b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/BlockstackSession.kt index 517cd7c..318c090 100644 --- a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/BlockstackSession.kt +++ b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/BlockstackSession.kt @@ -60,7 +60,7 @@ class BlockstackSession(private val sessionStore: ISessionStore, private val app */ suspend fun handlePendingSignIn(authResponse: String): Result = withContext(dispatcher) { val transitKey = sessionStore.getTransitPrivateKey() - val nameLookupUrl = sessionStore.sessionData.json.optString("core-node", "https://core.blockstack.org") + val nameLookupUrl = sessionStore.sessionData.json.optString("core-node", "stacks-node-api.stacks.co") val tokenTriple = try { blockstack.decodeToken(authResponse) @@ -92,7 +92,11 @@ class BlockstackSession(private val sessionStore: ISessionStore, private val app } suspend fun handleUnencryptedSignIn(authResponse: String): Result { - val nameLookupUrl = sessionStore.sessionData.json.optString("core-node", "https://core.blockstack.org") + + val nameLookupUrl = sessionStore.sessionData.json.optString( + "core-node", + DEFAULT_CORE_API_ENDPOINT.replace("https://", "") + ) val tokenTriple = blockstack.decodeToken(authResponse) val tokenPayload = tokenTriple.second diff --git a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/DIDs.kt b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/DIDs.kt index 70c0489..349c68b 100644 --- a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/DIDs.kt +++ b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/DIDs.kt @@ -2,8 +2,6 @@ package org.blockstack.android.sdk import me.uport.sdk.universaldid.* import okhttp3.Call -import okhttp3.Request -import org.json.JSONObject import java.util.* class DIDs { @@ -19,6 +17,8 @@ class DIDs { if (didType == "btc-addr") { return did.split(':')[2] + }else if (didType == "stx-addr") { + return did.split(':')[2] } else { return null } diff --git a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Defaults.kt b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Defaults.kt index 770930e..18aa9e8 100644 --- a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Defaults.kt +++ b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/Defaults.kt @@ -1,7 +1,7 @@ package org.blockstack.android.sdk const val BLOCKSTACK_DEFAULT_GAIA_HUB_URL = "https://hub.blockstack.org" -const val DEFAULT_CORE_API_ENDPOINT = "https://core.blockstack.org" +const val DEFAULT_CORE_API_ENDPOINT = "https://stacks-node-api.stacks.co" const val DEFAULT_BLOCKSTACK_ID_HOST = "https://app.blockstack.org" const val LEGACY_BLOCKSTACK_ID_HOST = "https://browser.blockstack.org/auth" const val VERSION = "1.3.1" diff --git a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/extensions/Addresses.kt b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/extensions/Addresses.kt index b54a958..a860e62 100644 --- a/blockstack-sdk/src/main/java/org/blockstack/android/sdk/extensions/Addresses.kt +++ b/blockstack-sdk/src/main/java/org/blockstack/android/sdk/extensions/Addresses.kt @@ -15,20 +15,32 @@ fun ECKeyPair.toHexPublicKey64(): String { return this.getCompressedPublicKey().toNoPrefixHexString() } -fun ECKeyPair.toStxAddress(): String { +fun String.toStxAddress(sPrefix: Boolean = false): String { + val sha256 = hexToByteArray().sha256() + val hash160 = sha256.digestRipemd160() + val extended = "b0${hash160.toNoPrefixHexString()}" + val cs = checksum("16${hash160.toNoPrefixHexString()}") + + val prefix = if(sPrefix) "S" else "" + return prefix + (extended + cs).hexToByteArray().encodeCrockford32() +} + +fun ECKeyPair.toStxAddress(sPrefix: Boolean = false): String { val sha256 = toHexPublicKey64().hexToByteArray().sha256() val hash160 = sha256.digestRipemd160() val extended = "b0${hash160.toNoPrefixHexString()}" val cs = checksum("16${hash160.toNoPrefixHexString()}") - return (extended + cs).hexToByteArray().encodeCrockford32() + val prefix = if(sPrefix) "S" else "" + return prefix + (extended + cs).hexToByteArray().encodeCrockford32() } -fun ECKeyPair.toTestNetStxAddress() : String { +fun ECKeyPair.toTestNetStxAddress(sPrefix: Boolean = false) : String { val sha256 = toHexPublicKey64().hexToByteArray().sha256() val hash160 = sha256.digestRipemd160() val extended = "d0${hash160.toNoPrefixHexString()}" val cs = checksum("1a${hash160.toNoPrefixHexString()}") - return (extended + cs).hexToByteArray().encodeCrockford32() + val prefix = if(sPrefix) "S" else "" + return prefix + (extended + cs).hexToByteArray().encodeCrockford32() } fun String.toBtcAddress(): String { diff --git a/blockstack-sdk/src/test/java/org/blockstack/android/sdk/extensions/AddressesTest.kt b/blockstack-sdk/src/test/java/org/blockstack/android/sdk/extensions/AddressesTest.kt index 6aea3a5..7656ad8 100644 --- a/blockstack-sdk/src/test/java/org/blockstack/android/sdk/extensions/AddressesTest.kt +++ b/blockstack-sdk/src/test/java/org/blockstack/android/sdk/extensions/AddressesTest.kt @@ -37,6 +37,7 @@ class AddressesTest { Assert.assertEquals(PRIVATE_KEY, keys.keyPair.privateKey.key.toHexStringNoPrefix()) Assert.assertEquals(BTC_ADDRESS_MAINNET, keys.keyPair.toBtcAddress()) Assert.assertEquals(STX_ADDRESS_MAINNET, "S${keys.keyPair.toStxAddress()}") + Assert.assertEquals(STX_ADDRESS_MAINNET, keys.keyPair.toStxAddress(true)) } @Test @@ -46,6 +47,7 @@ class AddressesTest { // Act Assert Assert.assertEquals(STX_ADDRESS_TESTNET, "S${keys.keyPair.toTestNetStxAddress()}") + Assert.assertEquals(STX_ADDRESS_TESTNET, keys.keyPair.toTestNetStxAddress(true)) } diff --git a/example/src/main/java/org/blockstack/android/MainActivity.kt b/example/src/main/java/org/blockstack/android/MainActivity.kt index 47c3efd..57081e8 100644 --- a/example/src/main/java/org/blockstack/android/MainActivity.kt +++ b/example/src/main/java/org/blockstack/android/MainActivity.kt @@ -186,8 +186,7 @@ class MainActivity : AppCompatActivity() { } getStringFileFromUserButton.setOnClickListener { - - val zoneFileLookupUrl = URL("https://core.blockstack.org/v1/names") + val zoneFileLookupUrl = URL("https://stacks-node-api.stacks.co/v1/names") fileFromUserContentsTextView.text = "Downloading file from other user..." lifecycleScope.launch { val profile = blockstack.lookupProfile(username, zoneFileLookupURL = zoneFileLookupUrl) @@ -225,7 +224,7 @@ class MainActivity : AppCompatActivity() { getUserAppFileUrlButton.setOnClickListener { _ -> getUserAppFileUrlText.text = "Getting url ..." - val zoneFileLookupUrl = "https://core.blockstack.org/v1/names" + val zoneFileLookupUrl = DEFAULT_CORE_API_ENDPOINT + "v1/names" lifecycleScope.launch { val it = blockstack.getUserAppFileUrl(textFileName, username, "https://flamboyant-darwin-d11c17.netlify.app", zoneFileLookupUrl) withContext(Dispatchers.Main) {