Skip to content

Commit

Permalink
Combine full extrinsic and signature creation (#96)
Browse files Browse the repository at this point in the history
* Reworked build extrinsic for consistency and convinience

* Bump version

* Code style
  • Loading branch information
valentunn authored Oct 4, 2024
1 parent c43c1cf commit 132aeb5
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 96 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 = '2.1.4'
versionName = '2.2.0'
versionCode = 1

// SDK and tools
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.Add
import io.novasama.substrate_sdk_android.runtime.definitions.types.instances.SignatureInstanceConstructor
import io.novasama.substrate_sdk_android.runtime.definitions.types.toHex
import io.novasama.substrate_sdk_android.runtime.definitions.types.toHexUntyped
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SendableExtrinsic
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignedExtrinsic
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.Signer
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.SignerPayloadExtrinsic
Expand Down Expand Up @@ -87,12 +88,6 @@ class ExtrinsicBuilder(
_customSignedExtensions[id] = value
}

@Deprecated(
message = "Use restCalls() for better readability",
replaceWith = ReplaceWith(expression = "resetCalls()")
)
fun reset(): ExtrinsicBuilder = resetCalls()

fun resetCalls(): ExtrinsicBuilder {
calls.clear()

Expand All @@ -105,70 +100,18 @@ class ExtrinsicBuilder(
return maybeWrapInBatch(batchMode)
}

suspend fun build(
suspend fun buildExtrinsic(
batchMode: BatchMode = BatchMode.BATCH
): String {
): SendableExtrinsic {
val call = maybeWrapInBatch(batchMode)

return build(CallRepresentation.Instance(call))
return buildSendableExtrinsic(CallRepresentation.Instance(call))
}

suspend fun build(
suspend fun buildExtrinsic(
rawCallBytes: ByteArray
): String {
): SendableExtrinsic {
requireNotMixingBytesAndInstanceCalls()

return build(CallRepresentation.Bytes(rawCallBytes))
}

suspend fun buildSignature(
batchMode: BatchMode = BatchMode.BATCH
): String {
val call = maybeWrapInBatch(batchMode)

return buildSignature(CallRepresentation.Instance(call))
}

suspend fun buildSignature(
rawCallBytes: ByteArray
): String {
requireNotMixingBytesAndInstanceCalls()

return buildSignature(CallRepresentation.Bytes(rawCallBytes))
}

private suspend fun build(callRepresentation: CallRepresentation): String {
val signedExtrinsic = buildSignedExtrinsic(callRepresentation)

val multiSignature = signatureConstructor.constructInstance(
runtime.typeRegistry,
signedExtrinsic.signatureWrapper
)

val extrinsic = Extrinsic.EncodingInstance(
signature = Extrinsic.Signature.new(
accountIdentifier = buildEncodableAddressInstance(signedExtrinsic.payload.accountId),
signature = multiSignature,
signedExtras = signedExtrinsic.payload.signedExtras.includedInExtrinsic
),
callRepresentation = signedExtrinsic.payload.call
)

return Extrinsic.toHex(runtime, extrinsic)
}

private suspend fun buildSignature(
callRepresentation: CallRepresentation
): String {
val signedExtrinsic = buildSignedExtrinsic(callRepresentation)
val multiSignature = signatureConstructor.constructInstance(
runtime.typeRegistry,
signedExtrinsic.signatureWrapper
)

val signatureType = Extrinsic.signatureType(runtime)

return signatureType.toHexUntyped(runtime, multiSignature)
return buildSendableExtrinsic(CallRepresentation.Bytes(rawCallBytes))
}

private fun maybeWrapInBatch(batchMode: BatchMode): GenericCall.Instance {
Expand All @@ -179,7 +122,9 @@ class ExtrinsicBuilder(
}
}

private suspend fun buildSignedExtrinsic(callRepresentation: CallRepresentation): SignedExtrinsic {
private suspend fun buildSendableExtrinsic(
callRepresentation: CallRepresentation
): SendableExtrinsic {
val signerPayload = SignerPayloadExtrinsic(
runtime = runtime,
accountId = accountId,
Expand All @@ -192,7 +137,9 @@ class ExtrinsicBuilder(
nonce = nonce
)

return signer.signExtrinsic(signerPayload)
val signedExtrinsic = signer.signExtrinsic(signerPayload)

return RealSendableExtrinsic(signedExtrinsic)
}

private fun buildIncludedInSignature(): Map<String, Any?> {
Expand Down Expand Up @@ -257,4 +204,41 @@ class ExtrinsicBuilder(
"Cannot mix instance and raw bytes calls"
}
}

private inner class RealSendableExtrinsic(
private val signedExtrinsic: SignedExtrinsic
) : SendableExtrinsic {

private val multiSignature = signatureConstructor.constructInstance(
runtime.typeRegistry,
signedExtrinsic.signatureWrapper
)

override val extrinsicHex by lazy {
createExtrinsicHex()
}

override val signatureHex by lazy {
createSignatureHex()
}

private fun createExtrinsicHex(): String {
val address = buildEncodableAddressInstance(signedExtrinsic.payload.accountId)
val extrinsic = Extrinsic.EncodingInstance(
signature = Extrinsic.Signature.new(
accountIdentifier = address,
signature = multiSignature,
signedExtras = signedExtrinsic.payload.signedExtras.includedInExtrinsic
),
callRepresentation = signedExtrinsic.payload.call
)

return Extrinsic.toHex(runtime, extrinsic)
}

private fun createSignatureHex(): String {
val signatureType = Extrinsic.signatureType(runtime)
return signatureType.toHexUntyped(runtime, multiSignature)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.novasama.substrate_sdk_android.runtime.extrinsic.signer

interface SendableExtrinsic {

val signatureHex: String

val extrinsicHex: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ class SendIntegrationTest : BaseIntegrationTest(WESTEND_URL) {
)
}

val extrinsic = builder.build()
val extrinsic = builder.buildExtrinsic()

print(socketService.executeAsync(SubmitExtrinsicRequest(extrinsic)).result!!)
print(socketService.executeAsync(SubmitExtrinsicRequest(extrinsic.extrinsicHex)).result!!)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.Era
import io.novasama.substrate_sdk_android.runtime.definitions.types.generics.Extrinsic
import io.novasama.substrate_sdk_android.runtime.extrinsic.signer.KeyPairSigner
import io.novasama.substrate_sdk_android.runtime.metadata.MetadataTestCommon
import io.novasama.substrate_sdk_android.runtime.metadata.RuntimeMetadata
import io.novasama.substrate_sdk_android.runtime.metadata.SignedExtensionValue
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.publicKeyToSubstrateAccountId
import io.novasama.substrate_sdk_android.ss58.SS58Encoder.toAccountId
Expand Down Expand Up @@ -52,35 +51,35 @@ class ExtrinsicBuilderTest {
@Test
fun `should build extrinsic from raw call bytes`() = runBlockingTest {
val extrinsic = createExtrinsicBuilder()
.build(TRANSFER_CALL_BYTES)
.buildExtrinsic(TRANSFER_CALL_BYTES)

assertEquals(SINGLE_TRANSFER_EXTRINSIC, extrinsic)
assertEquals(SINGLE_TRANSFER_EXTRINSIC, extrinsic.extrinsicHex)
}

@Test
fun `should build single transfer extrinsic`() = runBlockingTest {
val encoded = createExtrinsicBuilder()
val extrinsic = createExtrinsicBuilder()
.testSingleTransfer()
.build()
.buildExtrinsic()

assertEquals(SINGLE_TRANSFER_EXTRINSIC, encoded)
assertEquals(SINGLE_TRANSFER_EXTRINSIC, extrinsic.extrinsicHex)
}

@Test
fun `should build extrinsic signature from call instance`() = runBlockingTest {
val actualSignature = createExtrinsicBuilder()
val extrinsic = createExtrinsicBuilder()
.testSingleTransfer()
.buildSignature()
.buildExtrinsic()

assertEquals(EXTRINSIC_SIGNATURE, actualSignature)
assertEquals(EXTRINSIC_SIGNATURE, extrinsic.signatureHex)
}

@Test
fun `should build extrinsic signature from raw call bytes`() = runBlockingTest {
val extrinsic = createExtrinsicBuilder()
.buildSignature(TRANSFER_CALL_BYTES)
.buildExtrinsic(TRANSFER_CALL_BYTES)

assertEquals(EXTRINSIC_SIGNATURE, extrinsic)
assertEquals(EXTRINSIC_SIGNATURE, extrinsic.signatureHex)
}

@Test
Expand All @@ -89,16 +88,16 @@ class ExtrinsicBuilderTest {
val recipientAccountId =
"340a806419d5e278172e45cb0e50da1b031795366c99ddfe0a680bd53b142c63".fromHex()

val encoded = createExtrinsicBuilder()
val extrinsic = createExtrinsicBuilder()
.transfer(
recipientAccountId = recipientAccountId,
amount = wrongAMount
)
.reset()
.resetCalls()
.testSingleTransfer()
.build()
.buildExtrinsic()

assertEquals(SINGLE_TRANSFER_EXTRINSIC, encoded)
assertEquals(SINGLE_TRANSFER_EXTRINSIC, extrinsic.extrinsicHex)
}

@Test
Expand All @@ -115,9 +114,9 @@ class ExtrinsicBuilderTest {
)
}

val encoded = builder.build()
val extrinsic = builder.buildExtrinsic()

assertEquals(extrinsicInHex, encoded)
assertEquals(extrinsicInHex, extrinsic.extrinsicHex)
}

@Test
Expand All @@ -131,8 +130,8 @@ class ExtrinsicBuilderTest {
)
}

val encoded = extrinsicBuilder.build(batchMode = BatchMode.BATCH_ALL)
val decoded = Extrinsic.fromHex(runtime, encoded)
val extrinsic = extrinsicBuilder.buildExtrinsic(batchMode = BatchMode.BATCH_ALL)
val decoded = Extrinsic.fromHex(runtime, extrinsic.extrinsicHex)

assertEquals(decoded.call.function.name, "batch_all")
}
Expand All @@ -148,9 +147,9 @@ class ExtrinsicBuilderTest {
)
}

val encoded = extrinsicBuilder.build(batchMode = BatchMode.BATCH_ALL)
val extrinsic = extrinsicBuilder.buildExtrinsic(batchMode = BatchMode.BATCH_ALL)

assertEquals(BIG_TRANSACTION, encoded)
assertEquals(BIG_TRANSACTION, extrinsic.extrinsicHex)
}

@Test
Expand Down Expand Up @@ -186,9 +185,9 @@ class ExtrinsicBuilderTest {
amount = BigInteger("10000000000")
)

val encoded = builder.build()

assertEquals(extrinsicInHex, encoded)
val extrinsic = builder.buildExtrinsic()
assertEquals(extrinsicInHex, extrinsic.extrinsicHex)
}

@Test
Expand Down Expand Up @@ -227,9 +226,9 @@ class ExtrinsicBuilderTest {
value = SignedExtensionValue(includedInExtrinsic = chargeAssetTxPaymentValue)
)

val encoded = builder.build()
val extrinsic = builder.buildExtrinsic()

assertEquals(extrinsicInHex, encoded)
assertEquals(extrinsicInHex, extrinsic.extrinsicHex)
}

@Test
Expand All @@ -238,7 +237,7 @@ class ExtrinsicBuilderTest {

createExtrinsicBuilder(runtime)
.testSingleTransfer()
.build()
.buildExtrinsic()
}

private fun createExtrinsicBuilder(usedRuntime: RuntimeSnapshot = runtime) = ExtrinsicBuilder(
Expand Down

0 comments on commit 132aeb5

Please sign in to comment.