Skip to content

Commit

Permalink
Set correct PublicKey path
Browse files Browse the repository at this point in the history
  • Loading branch information
omurovch committed Mar 15, 2024
1 parent d725bf1 commit fbf798e
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ open class PublicKey() {
}

constructor(account: Int, index: Int, external: Boolean, publicKey: ByteArray, publicKeyHash: ByteArray) : this() {
this.path = "$account/${if (external) 1 else 0}/$index"
this.path = "$account/${if (external) 0 else 1}/$index"
this.account = account
this.index = index
this.external = external
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import io.horizontalsystems.bitcoincore.core.IStorage
import io.horizontalsystems.bitcoincore.core.PluginManager
import io.horizontalsystems.bitcoincore.extensions.toReversedByteArray
import io.horizontalsystems.bitcoincore.extensions.toReversedHex
import io.horizontalsystems.bitcoincore.managers.PublicKeyManager
import io.horizontalsystems.bitcoincore.managers.UnspentOutputProvider
import io.horizontalsystems.bitcoincore.models.Address
import io.horizontalsystems.bitcoincore.models.PublicKey
Expand Down Expand Up @@ -102,23 +101,9 @@ class ReplacementTransactionBuilder(
originalInputs.map { inputWithPreviousOutput ->
val previousOutput =
inputWithPreviousOutput.previousOutput ?: throw BuildError.InvalidTransaction("No previous output of original transaction")
val prevOutputPublicKey = previousOutput.publicKeyPath?.let { path ->
val parts = path.split("/").map { it.toInt() }
if (parts.size != 3) throw PublicKeyManager.Error.InvalidPath
val account = parts[0]
val change = if (parts[1] == 0) 1 else 0 // change was incorrectly set in PublicKey
val index = parts[2]
val fixedPath = "$account/$change/$index"

publicKeyManager.getPublicKeyByPath(fixedPath)
} ?: throw BuildError.InvalidTransaction("No public key of original transaction")
mutableTransaction.addInput(
inputToSign(
previousOutput = previousOutput,
prevOutputPublicKey = prevOutputPublicKey,
sequence = incrementedSequence(inputWithPreviousOutput)
)
)
val publicKey = previousOutput.publicKeyPath?.let { publicKeyManager.getPublicKeyByPath(it) }
?: throw BuildError.InvalidTransaction("No public key of original transaction")
mutableTransaction.addInput(inputToSign(previousOutput = previousOutput, publicKey, incrementedSequence(inputWithPreviousOutput)))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import android.content.Context
import io.horizontalsystems.bitcoincore.models.*
import io.horizontalsystems.bitcoincore.storage.migrations.*

@Database(version = 18, exportSchema = false, entities = [
@Database(version = 19, exportSchema = false, entities = [
BlockchainState::class,
PeerAddress::class,
BlockHash::class,
Expand Down Expand Up @@ -49,6 +49,7 @@ abstract class CoreDatabase : RoomDatabase() {
return Room.databaseBuilder(context, CoreDatabase::class.java, dbName)
.allowMainThreadQueries()
.addMigrations(
Migration_18_19,
Migration_17_18,
Migration_16_17,
Migration_15_16,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package io.horizontalsystems.bitcoincore.storage.migrations

import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import android.util.Log
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import io.horizontalsystems.bitcoincore.managers.PublicKeyManager

object Migration_18_19 : Migration(18, 19) {

override fun migrate(database: SupportSQLiteDatabase) {
try {
database.beginTransaction()

migratePublicKeyPath(database)
migrateTransactionOutput(database)
migrateBlockHashPublicKey(database)

database.setTransactionSuccessful()
} catch (error: Throwable) {
Log.e("e", "error in migration", error)
} finally {
database.endTransaction()
}
}

private fun migratePublicKeyPath(database: SupportSQLiteDatabase) {
var cursor = database.query("SELECT * FROM `PublicKey`")
val publicKeyPathIndex = cursor.getColumnIndex("path")

if (publicKeyPathIndex >= 0) {

while (cursor.moveToNext()) {
val path = cursor.getString(publicKeyPathIndex)
val fixedPath = fixedPath(path)

database.update(
/* table = */ "PublicKey",
/* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,
/* values = */ ContentValues().apply { put("path", "tmp-$fixedPath") },
/* whereClause = */ "path = ?",
/* whereArgs = */ arrayOf(path)
)
}

cursor = database.query("SELECT * FROM `PublicKey`")

while (cursor.moveToNext()) {
val path = cursor.getString(publicKeyPathIndex)
val fixedPath = path.removePrefix("tmp-")

database.update(
/* table = */ "PublicKey",
/* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,
/* values = */ ContentValues().apply { put("path", fixedPath) },
/* whereClause = */ "path = ?",
/* whereArgs = */ arrayOf(path)
)
}
}
}

private fun migrateTransactionOutput(database: SupportSQLiteDatabase) {
val cursor = database.query("SELECT * FROM `TransactionOutput` WHERE publicKeyPath IS NOT NULL")
val publicKeyPathIndex = cursor.getColumnIndex("publicKeyPath")
val transactionHashIndex = cursor.getColumnIndex("transactionHash")
val indexIndex = cursor.getColumnIndex("index")

if (publicKeyPathIndex >= 0 && transactionHashIndex >= 0 && indexIndex >= 0) {
while (cursor.moveToNext()) {
val transactionHash = cursor.getBlob(transactionHashIndex)
val index = cursor.getString(indexIndex)
val path = cursor.getString(publicKeyPathIndex)
val fixedPath = fixedPath(path)

database.update(
/* table = */ "TransactionOutput",
/* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,
/* values = */ ContentValues().apply { put("publicKeyPath", fixedPath) },
/* whereClause = */ "transactionHash = ? AND `index` = ?",
/* whereArgs = */ arrayOf(transactionHash, index)
)
}
}
}

private fun migrateBlockHashPublicKey(database: SupportSQLiteDatabase) {
val cursor = database.query("SELECT * FROM `BlockHashPublicKey` WHERE publicKeyPath IS NOT NULL")
val publicKeyPathIndex = cursor.getColumnIndex("publicKeyPath")
val blockHashIndex = cursor.getColumnIndex("blockHash")

if (publicKeyPathIndex >= 0 && blockHashIndex >= 0) {
while (cursor.moveToNext()) {
val blockHash = cursor.getBlob(blockHashIndex)
val path = cursor.getString(publicKeyPathIndex)
val fixedPath = fixedPath(path)

database.update(
/* table = */ "BlockHashPublicKey",
/* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,
/* values = */ ContentValues().apply {
put("publicKeyPath", fixedPath)
},
/* whereClause = */ "blockHash = ? AND publicKeyPath = ?",
/* whereArgs = */ arrayOf(blockHash, path)
)
}
}
}

private fun fixedPath(path: String): String {
val parts = path.split("/").map { it.toInt() }
if (parts.size != 3) return path
val account = parts[0]
val change = if (parts[1] == 0) 1 else 0
val index = parts[2]
return "$account/$change/$index"
}

}

0 comments on commit fbf798e

Please sign in to comment.