Skip to content

Commit

Permalink
Merge pull request #75 from novasamatech/develop
Browse files Browse the repository at this point in the history
* Detect RuntimeCall and RuntimeEvent in exotic paths

* Multi tx nonce (#72)

* Multi tx nonce

* Code style
  • Loading branch information
valentunn authored Jan 12, 2024
2 parents c15e656 + 9d96026 commit 327044a
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 32 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
buildscript {
ext {
// App version
versionName = '1.11.0'
versionName = '1.11.1'
versionCode = 1

// SDK and tools
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -185,6 +182,7 @@ class ExtrinsicBuilder(
call = callRepresentation,
signedExtras = signedExtrasInstance,
additionalSignedExtras = additionalSignedInstance,
nonce = nonce
)

return signer.signExtrinsic(signerPayload)
Expand Down Expand Up @@ -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) ->
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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))
Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -29,7 +30,8 @@ data class SignerPayloadExtrinsic(
val accountId: AccountId,
val call: CallRepresentation,
val signedExtras: Map<String, Any?>,
val additionalSignedExtras: Map<String, Any?>
val additionalSignedExtras: Map<String, Any?>,
val nonce: Nonce,
)

fun SignerPayloadRaw.Companion.fromUtf8(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down

0 comments on commit 327044a

Please sign in to comment.