From 4afc2e01bcf62772bca716fc5372cb4a15bab6bc Mon Sep 17 00:00:00 2001 From: Valentun Date: Mon, 18 Dec 2023 16:01:49 +0300 Subject: [PATCH 1/2] Detect RuntimeCall and RuntimeEvent in exotic paths --- .../definitions/v14/typeMapping/SiTypeMapping.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/definitions/v14/typeMapping/SiTypeMapping.kt b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/definitions/v14/typeMapping/SiTypeMapping.kt index 177a09d4..8e40db68 100644 --- a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/definitions/v14/typeMapping/SiTypeMapping.kt +++ b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/definitions/v14/typeMapping/SiTypeMapping.kt @@ -208,10 +208,15 @@ private fun knownReplacements(): PathMatchTypeMapping { val eventAlias = AliasTo("GenericEvent") return PathMatchTypeMapping( - "*_runtime.RuntimeCall" to callAlias, - "*_runtime.Call" to callAlias, - "*_runtime.RuntimeEvent" to eventAlias, - "*_runtime.Event" to eventAlias, + // We can use broad wild-card since there should only be one `RuntimeCall` type in runtime + "*.RuntimeCall" to callAlias, + // We cannot use `*.Call` wild-card since per-pallet call enums + // have paths like `pallet_identity.pallet.Call` + // Chains that have paths not complying with this wild-card should be handled via types file + "*runtime.Call" to callAlias, + "*.RuntimeEvent" to eventAlias, + // Same wild card situation as per `runtime.Call` + "*runtime.Event" to eventAlias, "pallet_identity.types.Data" to AliasTo("Data"), "sp_runtime.generic.era.Era" to AliasTo("Era") From 9d9602645927d2950c0a98dfe815c78d58c8ab01 Mon Sep 17 00:00:00 2001 From: valentunn <70131744+valentunn@users.noreply.github.com> Date: Fri, 12 Jan 2024 14:05:15 +0700 Subject: [PATCH 2/2] Multi tx nonce (#72) * Multi tx nonce * Code style --- build.gradle | 2 +- .../runtime/extrinsic/ExtrinsicBuilder.kt | 25 ++------------ .../runtime/extrinsic/ExtrinsicBuilderExt.kt | 23 +++++++++++++ .../fearless_utils/runtime/extrinsic/Nonce.kt | 33 +++++++++++++++++++ .../runtime/extrinsic/signer/Signer.kt | 4 ++- .../extrinsic/SendIntegrationTest.kt | 3 +- .../runtime/extrinsic/ExtrinsicBuilderTest.kt | 6 ++-- 7 files changed, 68 insertions(+), 28 deletions(-) create mode 100644 fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/Nonce.kt diff --git a/build.gradle b/build.gradle index dd38e50e..71ae3d39 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { // App version - versionName = '1.11.0' + versionName = '1.11.1' versionCode = 1 // SDK and tools diff --git a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilder.kt b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilder.kt index 7e7e94b3..952343a4 100644 --- a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilder.kt +++ b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilder.kt @@ -4,7 +4,6 @@ import jp.co.soramitsu.fearless_utils.encrypt.SignatureWrapper import jp.co.soramitsu.fearless_utils.runtime.AccountId import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.fearless_utils.runtime.definitions.types.RuntimeType -import jp.co.soramitsu.fearless_utils.runtime.definitions.types.composite.Struct import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.DefaultSignedExtensions import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Era import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Extrinsic @@ -14,7 +13,6 @@ import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Generic import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.new import jp.co.soramitsu.fearless_utils.runtime.definitions.types.instances.AddressInstanceConstructor import jp.co.soramitsu.fearless_utils.runtime.definitions.types.instances.SignatureInstanceConstructor -import jp.co.soramitsu.fearless_utils.runtime.definitions.types.skipAliases import jp.co.soramitsu.fearless_utils.runtime.definitions.types.toHex import jp.co.soramitsu.fearless_utils.runtime.definitions.types.toHexUntyped import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignedExtrinsic @@ -23,7 +21,6 @@ import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadExtr import jp.co.soramitsu.fearless_utils.runtime.metadata.SignedExtensionId import jp.co.soramitsu.fearless_utils.runtime.metadata.SignedExtensionValue import jp.co.soramitsu.fearless_utils.runtime.metadata.call -import jp.co.soramitsu.fearless_utils.runtime.metadata.findSignedExtension import jp.co.soramitsu.fearless_utils.runtime.metadata.module import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.chain.RuntimeVersion import java.math.BigInteger @@ -32,7 +29,7 @@ private val DEFAULT_TIP = BigInteger.ZERO class ExtrinsicBuilder( val runtime: RuntimeSnapshot, - private val nonce: BigInteger, + private val nonce: Nonce, private val runtimeVersion: RuntimeVersion, private val genesisHash: ByteArray, private val accountId: AccountId, @@ -185,6 +182,7 @@ class ExtrinsicBuilder( call = callRepresentation, signedExtras = signedExtrasInstance, additionalSignedExtras = additionalSignedInstance, + nonce = nonce ) return signer.signExtrinsic(signerPayload) @@ -228,7 +226,7 @@ class ExtrinsicBuilder( val default = mapOf( DefaultSignedExtensions.CHECK_MORTALITY to era, DefaultSignedExtensions.CHECK_TX_PAYMENT to tip, - DefaultSignedExtensions.CHECK_NONCE to encodeNonce(nonce) + DefaultSignedExtensions.CHECK_NONCE to runtime.encodeNonce(nonce.nonce) ) val custom = _customSignedExtensions.mapValues { (_, extensionValues) -> @@ -238,23 +236,6 @@ class ExtrinsicBuilder( return default + custom } - private fun encodeNonce(nonce: BigInteger): Any { - val nonceExtension = runtime.metadata.extrinsic - .findSignedExtension(DefaultSignedExtensions.CHECK_NONCE) ?: return nonce - - val nonceType = nonceExtension.type?.skipAliases() - - return when { - nonceType is Struct && nonceType.mapping.size == 1 -> { - val fieldName = nonceType.mapping.keys.single() - - Struct.Instance(mapOf(fieldName to nonce)) - } - - else -> nonce - } - } - private fun requireNotMixingBytesAndInstanceCalls() { require(calls.isEmpty()) { "Cannot mix instance and raw bytes calls" diff --git a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderExt.kt b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderExt.kt index d6cf514f..4ecd8b69 100644 --- a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderExt.kt +++ b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderExt.kt @@ -1,7 +1,13 @@ package jp.co.soramitsu.fearless_utils.runtime.extrinsic +import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot +import jp.co.soramitsu.fearless_utils.runtime.definitions.types.composite.Struct +import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.DefaultSignedExtensions +import jp.co.soramitsu.fearless_utils.runtime.definitions.types.skipAliases import jp.co.soramitsu.fearless_utils.runtime.metadata.SignedExtensionId import jp.co.soramitsu.fearless_utils.runtime.metadata.SignedExtensionValue +import jp.co.soramitsu.fearless_utils.runtime.metadata.findSignedExtension +import java.math.BigInteger fun ExtrinsicBuilder.signedExtra(id: SignedExtensionId, value: Any?) { signedExtension(id, SignedExtensionValue(signedExtra = value)) @@ -10,3 +16,20 @@ fun ExtrinsicBuilder.signedExtra(id: SignedExtensionId, value: Any?) { fun ExtrinsicBuilder.additionalSigned(id: SignedExtensionId, value: Any?) { signedExtension(id, SignedExtensionValue(additionalSigned = value)) } + +fun RuntimeSnapshot.encodeNonce(nonce: BigInteger): Any { + val nonceExtension = metadata.extrinsic + .findSignedExtension(DefaultSignedExtensions.CHECK_NONCE) ?: return nonce + + val nonceType = nonceExtension.type?.skipAliases() + + return when { + nonceType is Struct && nonceType.mapping.size == 1 -> { + val fieldName = nonceType.mapping.keys.single() + + Struct.Instance(mapOf(fieldName to nonce)) + } + + else -> nonce + } +} diff --git a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/Nonce.kt b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/Nonce.kt new file mode 100644 index 00000000..a6ec4e28 --- /dev/null +++ b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/Nonce.kt @@ -0,0 +1,33 @@ +package jp.co.soramitsu.fearless_utils.runtime.extrinsic + +import java.math.BigInteger + +/** + * Structure for Nonce that is capable of carrying out information about multiple to-be-submitted txs + */ +data class Nonce( + /** + * Nonce that should be used to submit first tx + */ + val baseNonce: BigInteger, + /** + * Offset from the [baseNonce] for the case when multiple txs are being submitted + */ + val offset: BigInteger +) { + + val nonce: BigInteger = baseNonce + offset + + companion object { + + fun singleTx(nonce: BigInteger): Nonce { + return Nonce(baseNonce = nonce, offset = BigInteger.ZERO) + } + + fun zero(): Nonce = singleTx(BigInteger.ZERO) + } +} + +fun Nonce.replaceBaseNone(baseNonce: BigInteger): Nonce { + return copy(baseNonce = baseNonce) +} diff --git a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/signer/Signer.kt b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/signer/Signer.kt index e49b8030..f43bbbc5 100644 --- a/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/signer/Signer.kt +++ b/fearless-utils/src/main/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/signer/Signer.kt @@ -5,6 +5,7 @@ import jp.co.soramitsu.fearless_utils.extensions.fromHex import jp.co.soramitsu.fearless_utils.runtime.AccountId import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.Extrinsic.EncodingInstance.CallRepresentation +import jp.co.soramitsu.fearless_utils.runtime.extrinsic.Nonce class SignedExtrinsic( val payload: SignerPayloadExtrinsic, @@ -29,7 +30,8 @@ data class SignerPayloadExtrinsic( val accountId: AccountId, val call: CallRepresentation, val signedExtras: Map, - val additionalSignedExtras: Map + val additionalSignedExtras: Map, + val nonce: Nonce, ) fun SignerPayloadRaw.Companion.fromUtf8( diff --git a/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/integration/extrinsic/SendIntegrationTest.kt b/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/integration/extrinsic/SendIntegrationTest.kt index 3b0e604b..cf5387c7 100644 --- a/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/integration/extrinsic/SendIntegrationTest.kt +++ b/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/integration/extrinsic/SendIntegrationTest.kt @@ -11,6 +11,7 @@ import jp.co.soramitsu.fearless_utils.runtime.RealRuntimeProvider import jp.co.soramitsu.fearless_utils.runtime.definitions.types.composite.DictEnum import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.multiAddressFromId import jp.co.soramitsu.fearless_utils.runtime.extrinsic.ExtrinsicBuilder +import jp.co.soramitsu.fearless_utils.runtime.extrinsic.Nonce import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.KeyPairSigner import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder.publicKeyToSubstrateAccountId import jp.co.soramitsu.fearless_utils.wsrpc.executeAsync @@ -39,7 +40,7 @@ class SendIntegrationTest : BaseIntegrationTest(WESTEND_URL) { keypair = KEYPAIR, encryption = MultiChainEncryption.Substrate(EncryptionType.ED25519) ), - nonce = 38.toBigInteger(), + nonce = Nonce.singleTx(38.toBigInteger()), runtimeVersion = RuntimeVersion(48, 4), genesisHash = "e143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e".fromHex(), accountId = KEYPAIR.publicKey.publicKeyToSubstrateAccountId(), diff --git a/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderTest.kt b/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderTest.kt index 6351e438..0c966d32 100644 --- a/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderTest.kt +++ b/fearless-utils/src/test/java/jp/co/soramitsu/fearless_utils/runtime/extrinsic/ExtrinsicBuilderTest.kt @@ -159,7 +159,7 @@ class ExtrinsicBuilderTest { val builder = ExtrinsicBuilder( runtime = runtime, - nonce = 34.toBigInteger(), + nonce = Nonce.singleTx(34.toBigInteger()), runtimeVersion = RuntimeVersion(601, 4), genesisHash = "48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a".fromHex(), signer = keypairSigner(), @@ -197,7 +197,7 @@ class ExtrinsicBuilderTest { val builder = ExtrinsicBuilder( runtime = runtime, - nonce = 34.toBigInteger(), + nonce = Nonce.singleTx(34.toBigInteger()), runtimeVersion = RuntimeVersion(601, 4), genesisHash = "48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a".fromHex(), signer = keypairSigner(), @@ -229,7 +229,7 @@ class ExtrinsicBuilderTest { private fun createExtrinsicBuilder() = ExtrinsicBuilder( runtime = runtime, signer = keypairSigner(), - nonce = 34.toBigInteger(), + nonce = Nonce.singleTx(34.toBigInteger()), runtimeVersion = RuntimeVersion(48, 4), genesisHash = "e143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e".fromHex(), accountId = KEYPAIR.publicKey.publicKeyToSubstrateAccountId(),