Release Vexta Wallet v0.6.8 hotfix
This commit is contained in:
parent
3e2a7150f9
commit
45d9b111fb
6 changed files with 348 additions and 57 deletions
|
|
@ -22,8 +22,8 @@ android {
|
|||
applicationId = "org.vextaproject.wallet"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 37
|
||||
versionName = "0.6.7"
|
||||
versionCode = 38
|
||||
versionName = "0.6.8"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ object BlockScanner {
|
|||
val height: Int,
|
||||
val netSatoshis: Long,
|
||||
val blockTime: Long,
|
||||
val isCoinbase: Boolean = false,
|
||||
val isPending: Boolean = false,
|
||||
val feeSatoshis: Long? = null,
|
||||
val recipientAddress: String? = null
|
||||
|
|
@ -263,7 +264,8 @@ object BlockScanner {
|
|||
.toHex(),
|
||||
height = height,
|
||||
netSatoshis = netSatoshis,
|
||||
blockTime = blockTime
|
||||
blockTime = blockTime,
|
||||
isCoinbase = transaction.isCoinbase
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ object HeaderSync {
|
|||
var versionInfo: VersionInfo? = null
|
||||
var gotVerack = false
|
||||
var requested = false
|
||||
var totalReceived = 0
|
||||
val cachedBefore = chain.last().height
|
||||
|
||||
repeat(200) {
|
||||
|
|
@ -153,6 +154,8 @@ object HeaderSync {
|
|||
|
||||
"headers" -> {
|
||||
val received = parseHeaders(message.second, chain)
|
||||
totalReceived += received
|
||||
|
||||
val remote = versionInfo
|
||||
?: throw IllegalStateException("missing node version")
|
||||
|
||||
|
|
@ -162,8 +165,25 @@ object HeaderSync {
|
|||
)
|
||||
}
|
||||
|
||||
if (chain.last().height < remote.height) {
|
||||
if (received == 0) {
|
||||
throw IllegalStateException(
|
||||
"peer advertises height ${remote.height} " +
|
||||
"but returned no additional headers"
|
||||
)
|
||||
}
|
||||
|
||||
sendMessage(
|
||||
output,
|
||||
"getheaders",
|
||||
createGetHeadersPayload(chain)
|
||||
)
|
||||
|
||||
return@repeat
|
||||
}
|
||||
|
||||
return SyncResult(
|
||||
received = received,
|
||||
received = totalReceived,
|
||||
cachedBefore = cachedBefore,
|
||||
chainHeight = chain.last().height,
|
||||
lastHash = chain.last().hashDisplay
|
||||
|
|
@ -175,7 +195,7 @@ object HeaderSync {
|
|||
sendMessage(
|
||||
output,
|
||||
"getheaders",
|
||||
createGetHeadersPayload(chain.last().hashWire)
|
||||
createGetHeadersPayload(chain)
|
||||
)
|
||||
requested = true
|
||||
}
|
||||
|
|
@ -201,6 +221,8 @@ object HeaderSync {
|
|||
)
|
||||
}
|
||||
|
||||
val receivedHeaders = mutableListOf<ByteArray>()
|
||||
|
||||
repeat(count) {
|
||||
if (buffer.remaining() < 81) {
|
||||
throw IllegalStateException("truncated headers message")
|
||||
|
|
@ -217,8 +239,34 @@ object HeaderSync {
|
|||
)
|
||||
}
|
||||
|
||||
receivedHeaders.add(raw)
|
||||
}
|
||||
|
||||
if (receivedHeaders.isNotEmpty()) {
|
||||
val firstPreviousHash =
|
||||
receivedHeaders.first().copyOfRange(4, 36)
|
||||
|
||||
if (!firstPreviousHash.contentEquals(chain.last().hashWire)) {
|
||||
val ancestorIndex =
|
||||
chain.indexOfLast {
|
||||
it.hashWire.contentEquals(firstPreviousHash)
|
||||
}
|
||||
|
||||
if (ancestorIndex < 0) {
|
||||
throw IllegalStateException(
|
||||
"peer returned headers with no common ancestor"
|
||||
)
|
||||
}
|
||||
|
||||
while (chain.lastIndex > ancestorIndex) {
|
||||
chain.removeAt(chain.lastIndex)
|
||||
}
|
||||
}
|
||||
|
||||
receivedHeaders.forEach { raw ->
|
||||
appendVerifiedHeader(chain, raw)
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
|
@ -375,12 +423,38 @@ object HeaderSync {
|
|||
return compact
|
||||
}
|
||||
|
||||
fun createGetHeadersPayload(locatorHash: ByteArray): ByteArray {
|
||||
fun createGetHeadersPayload(chain: List<ChainHeader>): ByteArray {
|
||||
val out = ByteArrayOutputStream()
|
||||
val locator = mutableListOf<ByteArray>()
|
||||
|
||||
var index = chain.lastIndex
|
||||
var step = 1
|
||||
|
||||
while (index >= 0) {
|
||||
locator.add(chain[index].hashWire)
|
||||
|
||||
if (index == 0) {
|
||||
break
|
||||
}
|
||||
|
||||
index = (index - step).coerceAtLeast(0)
|
||||
|
||||
if (locator.size >= 10) {
|
||||
step *= 2
|
||||
}
|
||||
}
|
||||
|
||||
if (!locator.last().contentEquals(chain.first().hashWire)) {
|
||||
locator.add(chain.first().hashWire)
|
||||
}
|
||||
|
||||
writeInt32LE(out, PROTOCOL_VERSION)
|
||||
writeCompactSize(out, 1)
|
||||
out.write(locatorHash)
|
||||
writeCompactSize(out, locator.size)
|
||||
|
||||
locator.forEach { hash ->
|
||||
out.write(hash)
|
||||
}
|
||||
|
||||
out.write(ByteArray(32))
|
||||
|
||||
return out.toByteArray()
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ class MainActivity : Activity() {
|
|||
)
|
||||
|
||||
val results = mutableListOf<String>()
|
||||
var successfulPeerSyncs = 0
|
||||
|
||||
for ((index, peer) in peers.withIndex()) {
|
||||
try {
|
||||
|
|
@ -128,6 +129,8 @@ class MainActivity : Activity() {
|
|||
saveChain(chain)
|
||||
}
|
||||
|
||||
successfulPeerSyncs++
|
||||
|
||||
results.add(
|
||||
"$peer:$PORT\n" +
|
||||
"Handshake: successful\n" +
|
||||
|
|
@ -148,7 +151,10 @@ class MainActivity : Activity() {
|
|||
|
||||
runOnUiThread {
|
||||
if (intent.getBooleanExtra("sync_only", false)) {
|
||||
if (chain.last().height > 0) {
|
||||
if (
|
||||
successfulPeerSyncs > 0 &&
|
||||
chain.last().height > 0
|
||||
) {
|
||||
setResult(
|
||||
RESULT_OK,
|
||||
android.content.Intent().putExtra(
|
||||
|
|
@ -271,6 +277,7 @@ class MainActivity : Activity() {
|
|||
var versionInfo: VersionInfo? = null
|
||||
var gotVerack = false
|
||||
var requested = false
|
||||
var totalReceived = 0
|
||||
val cachedBefore = chain.last().height
|
||||
|
||||
repeat(200) {
|
||||
|
|
@ -288,6 +295,8 @@ class MainActivity : Activity() {
|
|||
|
||||
"headers" -> {
|
||||
val received = parseHeaders(message.second, chain)
|
||||
totalReceived += received
|
||||
|
||||
val remote = versionInfo
|
||||
?: throw IllegalStateException("missing node version")
|
||||
|
||||
|
|
@ -297,8 +306,25 @@ class MainActivity : Activity() {
|
|||
)
|
||||
}
|
||||
|
||||
if (chain.last().height < remote.height) {
|
||||
if (received == 0) {
|
||||
throw IllegalStateException(
|
||||
"peer advertises height ${remote.height} " +
|
||||
"but returned no additional headers"
|
||||
)
|
||||
}
|
||||
|
||||
sendMessage(
|
||||
output,
|
||||
"getheaders",
|
||||
createGetHeadersPayload(chain)
|
||||
)
|
||||
|
||||
return@repeat
|
||||
}
|
||||
|
||||
return SyncResult(
|
||||
received = received,
|
||||
received = totalReceived,
|
||||
cachedBefore = cachedBefore,
|
||||
chainHeight = chain.last().height,
|
||||
lastHash = chain.last().hashDisplay
|
||||
|
|
@ -310,7 +336,7 @@ class MainActivity : Activity() {
|
|||
sendMessage(
|
||||
output,
|
||||
"getheaders",
|
||||
createGetHeadersPayload(chain.last().hashWire)
|
||||
createGetHeadersPayload(chain)
|
||||
)
|
||||
requested = true
|
||||
}
|
||||
|
|
@ -336,6 +362,8 @@ class MainActivity : Activity() {
|
|||
)
|
||||
}
|
||||
|
||||
val receivedHeaders = mutableListOf<ByteArray>()
|
||||
|
||||
repeat(count) {
|
||||
if (buffer.remaining() < 81) {
|
||||
throw IllegalStateException("truncated headers message")
|
||||
|
|
@ -352,8 +380,34 @@ class MainActivity : Activity() {
|
|||
)
|
||||
}
|
||||
|
||||
receivedHeaders.add(raw)
|
||||
}
|
||||
|
||||
if (receivedHeaders.isNotEmpty()) {
|
||||
val firstPreviousHash =
|
||||
receivedHeaders.first().copyOfRange(4, 36)
|
||||
|
||||
if (!firstPreviousHash.contentEquals(chain.last().hashWire)) {
|
||||
val ancestorIndex =
|
||||
chain.indexOfLast {
|
||||
it.hashWire.contentEquals(firstPreviousHash)
|
||||
}
|
||||
|
||||
if (ancestorIndex < 0) {
|
||||
throw IllegalStateException(
|
||||
"peer returned headers with no common ancestor"
|
||||
)
|
||||
}
|
||||
|
||||
while (chain.lastIndex > ancestorIndex) {
|
||||
chain.removeAt(chain.lastIndex)
|
||||
}
|
||||
}
|
||||
|
||||
receivedHeaders.forEach { raw ->
|
||||
appendVerifiedHeader(chain, raw)
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
|
@ -510,12 +564,38 @@ class MainActivity : Activity() {
|
|||
return compact
|
||||
}
|
||||
|
||||
private fun createGetHeadersPayload(locatorHash: ByteArray): ByteArray {
|
||||
private fun createGetHeadersPayload(chain: List<ChainHeader>): ByteArray {
|
||||
val out = ByteArrayOutputStream()
|
||||
val locator = mutableListOf<ByteArray>()
|
||||
|
||||
var index = chain.lastIndex
|
||||
var step = 1
|
||||
|
||||
while (index >= 0) {
|
||||
locator.add(chain[index].hashWire)
|
||||
|
||||
if (index == 0) {
|
||||
break
|
||||
}
|
||||
|
||||
index = (index - step).coerceAtLeast(0)
|
||||
|
||||
if (locator.size >= 10) {
|
||||
step *= 2
|
||||
}
|
||||
}
|
||||
|
||||
if (!locator.last().contentEquals(chain.first().hashWire)) {
|
||||
locator.add(chain.first().hashWire)
|
||||
}
|
||||
|
||||
writeInt32LE(out, PROTOCOL_VERSION)
|
||||
writeCompactSize(out, 1)
|
||||
out.write(locatorHash)
|
||||
writeCompactSize(out, locator.size)
|
||||
|
||||
locator.forEach { hash ->
|
||||
out.write(hash)
|
||||
}
|
||||
|
||||
out.write(ByteArray(32))
|
||||
|
||||
return out.toByteArray()
|
||||
|
|
|
|||
|
|
@ -110,6 +110,11 @@ class WalletActivity : FragmentActivity() {
|
|||
"background_transaction_history"
|
||||
private const val PREF_LAST_BACKGROUND_SCAN_HEIGHT =
|
||||
"last_background_scan_height"
|
||||
private const val PREF_LAST_HEADER_HEIGHT =
|
||||
"last_header_height"
|
||||
private const val PREF_BACKGROUND_TX_HISTORY_VERSION =
|
||||
"background_transaction_history_version"
|
||||
private const val BACKGROUND_TX_HISTORY_VERSION = 4
|
||||
private const val PREF_FIAT_CURRENCY =
|
||||
"preferred_fiat_currency"
|
||||
|
||||
|
|
@ -147,6 +152,7 @@ class WalletActivity : FragmentActivity() {
|
|||
private var latestSpendableUtxos =
|
||||
emptyList<BlockScanner.SpendableUtxo>()
|
||||
private var latestChainHeight = 0
|
||||
private var walletFiatRefresh: ((Long) -> Unit)? = null
|
||||
|
||||
private fun spendableUtxos(
|
||||
utxos: List<BlockScanner.SpendableUtxo>,
|
||||
|
|
@ -193,7 +199,7 @@ class WalletActivity : FragmentActivity() {
|
|||
latestChainHeight =
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(
|
||||
PREF_LAST_BACKGROUND_SCAN_HEIGHT,
|
||||
PREF_LAST_HEADER_HEIGHT,
|
||||
0
|
||||
)
|
||||
.coerceAtLeast(0)
|
||||
|
|
@ -217,6 +223,10 @@ class WalletActivity : FragmentActivity() {
|
|||
100_000_000.0
|
||||
)
|
||||
|
||||
walletFiatRefresh?.invoke(
|
||||
balanceSatoshis
|
||||
)
|
||||
|
||||
walletHistoryView?.text =
|
||||
formatTransactionHistory(
|
||||
cachedTransactions,
|
||||
|
|
@ -429,7 +439,7 @@ class WalletActivity : FragmentActivity() {
|
|||
mainWalletVisible &&
|
||||
!blockchainScanRunning
|
||||
) {
|
||||
startHeaderSyncAndScan()
|
||||
startBackgroundHeaderSyncAndScan()
|
||||
}
|
||||
}, 400L)
|
||||
}
|
||||
|
|
@ -870,12 +880,21 @@ class WalletActivity : FragmentActivity() {
|
|||
}
|
||||
|
||||
blockchainScanRunning = true
|
||||
blockchainScanStatus?.text =
|
||||
"Synchronizing block headers..."
|
||||
|
||||
Thread {
|
||||
try {
|
||||
val chain = HeaderSync.loadCachedChain(this)
|
||||
|
||||
runOnUiThread {
|
||||
blockchainScanStatus?.text =
|
||||
"Synchronizing block headers...\n" +
|
||||
"Cached height: ${chain.last().height}"
|
||||
}
|
||||
|
||||
val syncErrors = mutableListOf<String>()
|
||||
var successfulPeerSyncs = 0
|
||||
|
||||
for (peer in listOf("87.106.99.23", "74.208.53.160")) {
|
||||
try {
|
||||
|
|
@ -884,6 +903,14 @@ class WalletActivity : FragmentActivity() {
|
|||
if (result.received > 0) {
|
||||
HeaderSync.saveChain(this, chain)
|
||||
}
|
||||
|
||||
successfulPeerSyncs++
|
||||
|
||||
runOnUiThread {
|
||||
blockchainScanStatus?.text =
|
||||
"Header sync OK: ${result.chainHeight}\n" +
|
||||
"Peer: $peer"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
syncErrors.add(
|
||||
"$peer: ${e.message ?: e.javaClass.simpleName}"
|
||||
|
|
@ -891,7 +918,10 @@ class WalletActivity : FragmentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
if (chain.last().height <= 0) {
|
||||
if (
|
||||
successfulPeerSyncs == 0 ||
|
||||
chain.last().height <= 0
|
||||
) {
|
||||
runOnUiThread {
|
||||
blockchainScanRunning = false
|
||||
blockchainScanStatus?.text =
|
||||
|
|
@ -904,9 +934,12 @@ class WalletActivity : FragmentActivity() {
|
|||
runOnUiThread {
|
||||
runBlockchainScan()
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
} catch (e: Exception) {
|
||||
runOnUiThread {
|
||||
blockchainScanRunning = false
|
||||
blockchainScanStatus?.text =
|
||||
"Background synchronization failed:\n" +
|
||||
(e.message ?: e.javaClass.simpleName)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
|
|
@ -947,6 +980,14 @@ class WalletActivity : FragmentActivity() {
|
|||
-1
|
||||
) ?: -1
|
||||
|
||||
if (height >= 0) {
|
||||
latestChainHeight = height
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.edit()
|
||||
.putInt(PREF_LAST_HEADER_HEIGHT, height)
|
||||
.apply()
|
||||
}
|
||||
|
||||
blockchainScanStatus?.text = if (height >= 0) {
|
||||
"Headers synchronized to block $height.\n" +
|
||||
"Scanning compact filters..."
|
||||
|
|
@ -1073,7 +1114,7 @@ class WalletActivity : FragmentActivity() {
|
|||
try {
|
||||
val parts = value.split("|")
|
||||
|
||||
if (parts.size != 4) {
|
||||
if (parts.size != 5) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
|
|
@ -1090,6 +1131,7 @@ class WalletActivity : FragmentActivity() {
|
|||
parts[2].toLong()
|
||||
},
|
||||
blockTime = parts[3].toLong(),
|
||||
isCoinbase = parts[4].toBooleanStrict(),
|
||||
feeSatoshis = outgoing?.feeSatoshis,
|
||||
recipientAddress =
|
||||
outgoing?.recipientAddress
|
||||
|
|
@ -1135,11 +1177,23 @@ class WalletActivity : FragmentActivity() {
|
|||
}
|
||||
|
||||
private fun hasCachedWalletState(): Boolean {
|
||||
return getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(
|
||||
val preferences =
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
|
||||
val scanHeight =
|
||||
preferences.getInt(
|
||||
PREF_LAST_BACKGROUND_SCAN_HEIGHT,
|
||||
0
|
||||
) > 0
|
||||
)
|
||||
|
||||
val cacheVersion =
|
||||
preferences.getInt(
|
||||
PREF_BACKGROUND_TX_HISTORY_VERSION,
|
||||
0
|
||||
)
|
||||
|
||||
return scanHeight > 0 &&
|
||||
cacheVersion == BACKGROUND_TX_HISTORY_VERSION
|
||||
}
|
||||
|
||||
private fun preferredFiatCurrency(): String {
|
||||
|
|
@ -1395,6 +1449,9 @@ class WalletActivity : FragmentActivity() {
|
|||
}
|
||||
},
|
||||
blockTime = newest.blockTime,
|
||||
isCoinbase = transactions.any {
|
||||
it.isCoinbase
|
||||
},
|
||||
feeSatoshis =
|
||||
outgoing?.feeSatoshis,
|
||||
recipientAddress =
|
||||
|
|
@ -1586,9 +1643,23 @@ class WalletActivity : FragmentActivity() {
|
|||
1
|
||||
).coerceAtLeast(1)
|
||||
|
||||
if (transaction.isCoinbase) {
|
||||
val maturityStatus =
|
||||
if (confirmations >= COINBASE_MATURITY) {
|
||||
"Coinbase reward — Mature"
|
||||
} else {
|
||||
"Coinbase reward — Immature"
|
||||
}
|
||||
|
||||
"Block ${transaction.height}\n" +
|
||||
"$maturityStatus\n" +
|
||||
"Confirmations: $confirmations / " +
|
||||
"$COINBASE_MATURITY"
|
||||
} else {
|
||||
"Block ${transaction.height}\n" +
|
||||
"Confirmations: $confirmations"
|
||||
}
|
||||
}
|
||||
|
||||
"$direction $sign$amount VTX\n" +
|
||||
"$dateTime\n" +
|
||||
|
|
@ -1637,17 +1708,45 @@ class WalletActivity : FragmentActivity() {
|
|||
|
||||
val details = StringBuilder()
|
||||
|
||||
details.append("Type: $direction\n")
|
||||
details.append(
|
||||
"Type: " +
|
||||
if (transaction.isCoinbase) {
|
||||
"Coinbase reward\n"
|
||||
} else {
|
||||
"$direction\n"
|
||||
}
|
||||
)
|
||||
details.append("Amount: $amount VTX\n")
|
||||
details.append("Date & time: $dateTime\n")
|
||||
|
||||
if (transaction.isPending) {
|
||||
details.append("Status: Pending\n")
|
||||
details.append("Confirmations: 0\n")
|
||||
} else {
|
||||
if (transaction.isCoinbase) {
|
||||
val mature =
|
||||
confirmations >= COINBASE_MATURITY
|
||||
|
||||
details.append(
|
||||
"Status: " +
|
||||
if (mature) {
|
||||
"Mature\n"
|
||||
} else {
|
||||
"Immature\n"
|
||||
}
|
||||
)
|
||||
details.append("Block: ${transaction.height}\n")
|
||||
details.append(
|
||||
"Confirmations: $confirmations / " +
|
||||
"$COINBASE_MATURITY\n"
|
||||
)
|
||||
} else {
|
||||
details.append("Status: Confirmed\n")
|
||||
details.append("Block: ${transaction.height}\n")
|
||||
details.append("Confirmations: $confirmations\n")
|
||||
details.append(
|
||||
"Confirmations: $confirmations\n"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
transaction.feeSatoshis?.let { fee ->
|
||||
|
|
@ -2144,7 +2243,7 @@ class WalletActivity : FragmentActivity() {
|
|||
latestChainHeight =
|
||||
getSharedPreferences(PREFS, MODE_PRIVATE)
|
||||
.getInt(
|
||||
PREF_LAST_BACKGROUND_SCAN_HEIGHT,
|
||||
PREF_LAST_HEADER_HEIGHT,
|
||||
0
|
||||
)
|
||||
.coerceAtLeast(0)
|
||||
|
|
@ -2337,8 +2436,10 @@ class WalletActivity : FragmentActivity() {
|
|||
|
||||
addView(alternativesContainer)
|
||||
|
||||
walletFiatRefresh = {
|
||||
balanceSatoshis ->
|
||||
loadFiatValues(
|
||||
cachedBalance
|
||||
balanceSatoshis
|
||||
) { values ->
|
||||
fiatValues = values
|
||||
|
||||
|
|
@ -2360,6 +2461,11 @@ class WalletActivity : FragmentActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walletFiatRefresh?.invoke(
|
||||
cachedBalance
|
||||
)
|
||||
|
||||
addView(space(8))
|
||||
addView(
|
||||
|
|
@ -2422,7 +2528,7 @@ class WalletActivity : FragmentActivity() {
|
|||
symbol = "↻",
|
||||
label = "Scan"
|
||||
) {
|
||||
startHeaderSyncAndScan()
|
||||
startBackgroundHeaderSyncAndScan()
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -2527,7 +2633,7 @@ class WalletActivity : FragmentActivity() {
|
|||
mainWalletVisible &&
|
||||
!blockchainScanRunning
|
||||
) {
|
||||
startHeaderSyncAndScan()
|
||||
startBackgroundHeaderSyncAndScan()
|
||||
}
|
||||
}, 500L)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,13 +43,15 @@ class WalletMonitoringService : Service() {
|
|||
"tx_notifications_initialized"
|
||||
private const val PREF_LAST_BACKGROUND_SCAN_HEIGHT =
|
||||
"last_background_scan_height"
|
||||
private const val PREF_LAST_HEADER_HEIGHT =
|
||||
"last_header_height"
|
||||
private const val PREF_BACKGROUND_UTXO_PREFIX =
|
||||
"background_utxos_"
|
||||
private const val PREF_BACKGROUND_TX_HISTORY =
|
||||
"background_transaction_history"
|
||||
private const val PREF_BACKGROUND_TX_HISTORY_VERSION =
|
||||
"background_transaction_history_version"
|
||||
private const val BACKGROUND_TX_HISTORY_VERSION = 2
|
||||
private const val BACKGROUND_TX_HISTORY_VERSION = 4
|
||||
}
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
|
@ -97,10 +99,24 @@ class WalletMonitoringService : Service() {
|
|||
try {
|
||||
val chain = HeaderSync.loadCachedChain(this)
|
||||
|
||||
val cachedTipHeightBeforeSync = chain.last().height
|
||||
val cachedTipHashBeforeSync = chain.last().hashDisplay
|
||||
var reorgDetected = false
|
||||
|
||||
for (peer in listOf("87.106.99.23", "74.208.53.160")) {
|
||||
try {
|
||||
val result = HeaderSync.syncFromPeer(peer, chain)
|
||||
|
||||
if (cachedTipHeightBeforeSync > 0) {
|
||||
val originalTipStillCanonical =
|
||||
chain.getOrNull(cachedTipHeightBeforeSync)
|
||||
?.hashDisplay == cachedTipHashBeforeSync
|
||||
|
||||
if (!originalTipStillCanonical) {
|
||||
reorgDetected = true
|
||||
}
|
||||
}
|
||||
|
||||
if (result.received > 0) {
|
||||
HeaderSync.saveChain(this, chain)
|
||||
}
|
||||
|
|
@ -122,6 +138,13 @@ class WalletMonitoringService : Service() {
|
|||
|
||||
val localHeight = chain.last().height
|
||||
|
||||
preferences.edit()
|
||||
.putInt(
|
||||
PREF_LAST_HEADER_HEIGHT,
|
||||
localHeight
|
||||
)
|
||||
.apply()
|
||||
|
||||
val previousHeight =
|
||||
preferences
|
||||
.getInt(
|
||||
|
|
@ -145,6 +168,7 @@ class WalletMonitoringService : Service() {
|
|||
) == BACKGROUND_TX_HISTORY_VERSION
|
||||
|
||||
val fullScanRequired =
|
||||
reorgDetected ||
|
||||
previousHeight <= 0 ||
|
||||
previousHeight > localHeight ||
|
||||
!backgroundCacheAvailable ||
|
||||
|
|
@ -226,7 +250,10 @@ class WalletMonitoringService : Service() {
|
|||
netSatoshis = transactions.sumOf {
|
||||
it.netSatoshis
|
||||
},
|
||||
blockTime = newest.blockTime
|
||||
blockTime = newest.blockTime,
|
||||
isCoinbase = transactions.any {
|
||||
it.isCoinbase
|
||||
}
|
||||
)
|
||||
}
|
||||
.filter { it.netSatoshis != 0L }
|
||||
|
|
@ -308,7 +335,8 @@ class WalletMonitoringService : Service() {
|
|||
transaction.txid,
|
||||
transaction.height.toString(),
|
||||
transaction.netSatoshis.toString(),
|
||||
transaction.blockTime.toString()
|
||||
transaction.blockTime.toString(),
|
||||
transaction.isCoinbase.toString()
|
||||
).joinToString("|")
|
||||
}.toSet()
|
||||
|
||||
|
|
@ -325,7 +353,7 @@ class WalletMonitoringService : Service() {
|
|||
try {
|
||||
val parts = value.split("|")
|
||||
|
||||
if (parts.size != 4) {
|
||||
if (parts.size != 5) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +361,8 @@ class WalletMonitoringService : Service() {
|
|||
txid = parts[0],
|
||||
height = parts[1].toInt(),
|
||||
netSatoshis = parts[2].toLong(),
|
||||
blockTime = parts[3].toLong()
|
||||
blockTime = parts[3].toLong(),
|
||||
isCoinbase = parts[4].toBooleanStrict()
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
|
|
|
|||
Loading…
Reference in a new issue