This commit is contained in:
SatoshiQaziMuhammed
2026-08-03 01:31:29 -07:00
committed by GitHub
3 changed files with 7 additions and 102 deletions
@@ -8,10 +8,8 @@ import io.novafoundation.nova.feature_account_api.data.signer.SigningContext
import io.novafoundation.nova.common.utils.min
import io.novafoundation.nova.feature_account_api.data.extrinsic.ExtrinsicSplitter
import io.novafoundation.nova.feature_account_api.data.extrinsic.SplitCalls
import io.novafoundation.nova.runtime.ext.isPezkuwiChain
import io.novafoundation.nova.runtime.ext.requireGenesisHash
import io.novafoundation.nova.runtime.extrinsic.CustomTransactionExtensions
import io.novafoundation.nova.runtime.extrinsic.extensions.PezkuwiCheckImmortal
import io.novafoundation.nova.runtime.extrinsic.multi.CallBuilder
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
@@ -135,6 +133,12 @@ internal class RealExtrinsicSplitter @Inject constructor(
return split
}
/**
* A throwaway signed extrinsic, built only to measure the weight of `call`.
*
* Immortal era: this extrinsic is never submitted, so there is nothing for a mortal
* era to protect, and an immortal one needs no block hash lookup.
*/
private suspend fun wrapInFakeExtrinsic(
signer: NovaSigner,
call: GenericCall.Instance,
@@ -148,15 +152,7 @@ internal class RealExtrinsicSplitter @Inject constructor(
extrinsicVersion = ExtrinsicVersion.V4,
batchMode = BatchMode.BATCH,
).apply {
// Use custom CheckMortality for Pezkuwi chains to avoid DictEnum type lookup issues.
// Gated on chain identity (not signed-extension presence): both Pezkuwi and Polkadot
// Asset Hub declare "AuthorizeCall", so that alone can't tell the chains apart, and
// PezkuwiCheckImmortal's raw DictEnum value fails Polkadot's own Era type codec.
if (chain.isPezkuwiChain) {
setTransactionExtension(PezkuwiCheckImmortal(genesisHash))
} else {
setTransactionExtension(CheckMortality(Era.Immortal, genesisHash))
}
setTransactionExtension(CheckMortality(Era.Immortal, genesisHash))
setTransactionExtension(CheckGenesis(chain.requireGenesisHash().fromHex()))
setTransactionExtension(ChargeTransactionPayment(BigInteger.ZERO))
setTransactionExtension(CheckMetadataHash(CheckMetadataHashMode.Disabled))
@@ -1,23 +0,0 @@
package io.novafoundation.nova.runtime.extrinsic.extensions
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.FixedValueTransactionExtension
/**
* Custom CheckMortality extension for Pezkuwi chains using IMMORTAL era.
*
* Pezkuwi uses pezsp_runtime.generic.era.Era which is a DictEnum with variants:
* - Immortal (encoded as 0x00)
* - Mortal1(u8), Mortal2(u8), ..., Mortal255(u8)
*
* This extension uses Immortal era with genesis hash, which matches how @pezkuwi/api signs.
*
* @param genesisHash The chain's genesis hash (32 bytes) for the signer payload
*/
class PezkuwiCheckImmortal(
genesisHash: ByteArray
) : FixedValueTransactionExtension(
name = "CheckMortality",
implicit = genesisHash, // Genesis hash goes into signer payload for immortal transactions
explicit = DictEnum.Entry<Any?>("Immortal", null) // Immortal variant - unit type with no value
)
@@ -1,68 +0,0 @@
package io.novafoundation.nova.runtime.extrinsic.extensions
import io.novasama.substrate_sdk_android.runtime.definitions.types.composite.DictEnum
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.Era
import io.novasama.substrate_sdk_android.runtime.extrinsic.v5.transactionExtension.extensions.FixedValueTransactionExtension
import java.math.BigInteger
/**
* Custom CheckMortality extension for Pezkuwi chains.
*
* Pezkuwi uses pezsp_runtime.generic.era.Era which is a DictEnum with variants:
* - Immortal
* - Mortal1(u8), Mortal2(u8), ..., Mortal255(u8)
*
* The variant name is "MortalX" where X is the first byte of the encoded era,
* and the variant's value is the second byte (u8).
*
* @param era The mortal era from MortalityConstructor
* @param blockHash The block hash (32 bytes) for the signer payload
*/
class PezkuwiCheckMortality(
era: Era.Mortal,
blockHash: ByteArray
) : FixedValueTransactionExtension(
name = "CheckMortality",
implicit = blockHash, // blockHash goes into signer payload
explicit = createEraEntry(era) // Era as DictEnum.Entry
) {
companion object {
/**
* Creates a DictEnum.Entry for the Era.
*
* Standard Era encoding produces 2 bytes:
* - First byte determines the variant name (Mortal1, Mortal2, ..., Mortal255)
* - Second byte is the variant's value (u8)
*/
private fun createEraEntry(era: Era.Mortal): DictEnum.Entry<BigInteger> {
val period = era.period.toLong()
val phase = era.phase.toLong()
val quantizeFactor = maxOf(period shr 12, 1)
// Calculate the two-byte encoding
val encoded = ((countTrailingZeroBits(period) - 1).coerceIn(1, 15)) or
((phase / quantizeFactor).toInt() shl 4)
val firstByte = encoded and 0xFF
val secondByte = (encoded shr 8) and 0xFF
// DictEnum variant: "MortalX" where X is the first byte
// Variant value: second byte as u8 (BigInteger)
return DictEnum.Entry(
name = "Mortal$firstByte",
value = BigInteger.valueOf(secondByte.toLong())
)
}
private fun countTrailingZeroBits(value: Long): Int {
if (value == 0L) return 64
var n = 0
var x = value
while ((x and 1L) == 0L) {
n++
x = x shr 1
}
return n
}
}
}