fix: check Namecoin name expiry before resolving
Query current block height via blockchain.headers.subscribe and reject names that have expired (>= 36000 blocks since last update). Populate expiresIn field in NameShowResult for downstream use. Unconfirmed transactions (height <= 0) are treated as active.
This commit is contained in:
+45
-2
@@ -131,6 +131,13 @@ class ElectrumxClient(
|
|||||||
|
|
||||||
private const val PROTOCOL_VERSION = "1.4"
|
private const val PROTOCOL_VERSION = "1.4"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namecoin names expire this many blocks after their last update.
|
||||||
|
* From chainparams.cpp: consensus.nNameExpirationDepth = 36000
|
||||||
|
* (~250 days at ~10 min/block).
|
||||||
|
*/
|
||||||
|
const val NAME_EXPIRE_DEPTH = 36_000
|
||||||
|
|
||||||
// Namecoin script opcodes
|
// Namecoin script opcodes
|
||||||
private const val OP_NAME_UPDATE: Byte = 0x53 // OP_3 repurposed by Namecoin
|
private const val OP_NAME_UPDATE: Byte = 0x53 // OP_3 repurposed by Namecoin
|
||||||
private const val OP_2DROP: Byte = 0x6d
|
private const val OP_2DROP: Byte = 0x6d
|
||||||
@@ -222,8 +229,28 @@ class ElectrumxClient(
|
|||||||
writer.println(txReq)
|
writer.println(txReq)
|
||||||
val txResponse = reader.readLine() ?: return null
|
val txResponse = reader.readLine() ?: return null
|
||||||
|
|
||||||
// 5. Parse the name value from the transaction
|
// 5. Get current block height to check name expiry
|
||||||
return parseNameFromTransaction(identifier, txHash, height, txResponse)
|
val headersReq = buildRpcRequest("blockchain.headers.subscribe", emptyList<String>())
|
||||||
|
writer.println(headersReq)
|
||||||
|
val headersResponse = reader.readLine()
|
||||||
|
val currentHeight = parseBlockHeight(headersResponse)
|
||||||
|
|
||||||
|
// 6. Check if the name has expired
|
||||||
|
if (currentHeight != null && height > 0) {
|
||||||
|
val blocksSinceUpdate = currentHeight - height
|
||||||
|
if (blocksSinceUpdate >= NAME_EXPIRE_DEPTH) {
|
||||||
|
return null // Name has expired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Parse the name value from the transaction
|
||||||
|
val result = parseNameFromTransaction(identifier, txHash, height, txResponse)
|
||||||
|
// Populate expiresIn if we know the current height
|
||||||
|
return if (result != null && currentHeight != null && height > 0) {
|
||||||
|
result.copy(expiresIn = NAME_EXPIRE_DEPTH - (currentHeight - height))
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
runCatching { writer.close() }
|
runCatching { writer.close() }
|
||||||
runCatching { reader.close() }
|
runCatching { reader.close() }
|
||||||
@@ -279,6 +306,22 @@ class ElectrumxClient(
|
|||||||
return digest.reversedArray().joinToString("") { "%02x".format(it) }
|
return digest.reversedArray().joinToString("") { "%02x".format(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the block height from a `blockchain.headers.subscribe` response.
|
||||||
|
*
|
||||||
|
* Response format: {"result": {"height": 814300, "hex": "..."}, ...}
|
||||||
|
*/
|
||||||
|
private fun parseBlockHeight(raw: String?): Int? {
|
||||||
|
if (raw == null) return null
|
||||||
|
return try {
|
||||||
|
val envelope = json.parseToJsonElement(raw).jsonObject
|
||||||
|
val result = envelope["result"]?.jsonObject ?: return null
|
||||||
|
result["height"]?.jsonPrimitive?.int
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the history response into a list of (txHash, height) pairs.
|
* Parse the history response into a list of (txHash, height) pairs.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user