Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cold-wallet-app/lib/screens/sign_transaction_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,20 @@ class _SignTransactionScreenState extends ConsumerState<SignTransactionScreen> {
});

try {
final parsed = _parsed;
if (parsed == null) {
setState(() {
_signing = false;
_error = 'Cannot sign an undecoded payload.';
});
return;
}
// Returns signature ++ publicKey; the hot wallet splits it and rebuilds the
// extrinsic via submitExtrinsicWithExternalSignature.
final signed = signMessageWithPubkey(
keypair: keypair,
message: QuantusSigningPayload.signablePayload(widget.request.payload),
specVersion: parsed.extensions.specVersion,
);
setState(() {
_signing = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ class _KeystoneSignatureScanScreenState extends ConsumerState<KeystoneSignatureS

Future<List<String>> _simulateSignature() async {
final keypair = await widget.session.account.getKeypair();
final signed = signMessageWithPubkey(keypair: keypair, message: widget.unsignedData.encodedPayloadToSign);
final signed = signMessageWithPubkey(
keypair: keypair,
message: widget.unsignedData.encodedPayloadToSign,
specVersion: widget.unsignedData.payloadToSign.specVersion,
);
return encodeUr(data: signed);
}

Expand Down
5 changes: 3 additions & 2 deletions quantus_sdk/lib/src/extensions/keypair_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ extension KeypairExtensions on crypto.Keypair {
String get ss58Address => crypto.toAccountId(obj: this);
Uint8List get addressBytes => Address.decode(ss58Address).pubkey;

/// Hedged (randomized) ML-DSA signature. Fresh entropy is generated per call.
Uint8List sign(List<int> message) => crypto.signMessage(keypair: this, message: message, entropy: _hedgeEntropy());
/// Hedged ML-DSA signature. [specVersion] 148+ uses QUANTUS_EXTRINSIC; earlier specs use none.
Uint8List sign(List<int> message, {required int specVersion}) =>
crypto.signMessage(keypair: this, message: message, entropy: _hedgeEntropy(), specVersion: specVersion);
}

crypto.U8Array32 _hedgeEntropy() {
Expand Down
45 changes: 37 additions & 8 deletions quantus_sdk/lib/src/rust/api/crypto.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,43 @@ String firstHashToAddress({required String firstHashHex}) =>
Keypair generateKeypairFromSeed({required List<int> seed}) =>
RustLib.instance.api.crateApiCryptoGenerateKeypairFromSeed(seed: seed);

Uint8List signMessage({required Keypair keypair, required List<int> message, U8Array32? entropy}) =>
RustLib.instance.api.crateApiCryptoSignMessage(keypair: keypair, message: message, entropy: entropy);

Uint8List signMessageWithPubkey({required Keypair keypair, required List<int> message, U8Array32? entropy}) =>
RustLib.instance.api.crateApiCryptoSignMessageWithPubkey(keypair: keypair, message: message, entropy: entropy);

bool verifyMessage({required Keypair keypair, required List<int> message, required List<int> signature}) =>
RustLib.instance.api.crateApiCryptoVerifyMessage(keypair: keypair, message: message, signature: signature);
/// Signs `message`. Spec 148+ uses [`signing_context::EXTRINSIC`]; earlier specs use none.
Uint8List signMessage({
required Keypair keypair,
required List<int> message,
U8Array32? entropy,
required int specVersion,
}) => RustLib.instance.api.crateApiCryptoSignMessage(
keypair: keypair,
message: message,
entropy: entropy,
specVersion: specVersion,
);

Uint8List signMessageWithPubkey({
required Keypair keypair,
required List<int> message,
U8Array32? entropy,
required int specVersion,
}) => RustLib.instance.api.crateApiCryptoSignMessageWithPubkey(
keypair: keypair,
message: message,
entropy: entropy,
specVersion: specVersion,
);

/// Verifies under the same context [`sign_message`] would use for `spec_version`.
bool verifyMessage({
required Keypair keypair,
required List<int> message,
required List<int> signature,
required int specVersion,
}) => RustLib.instance.api.crateApiCryptoVerifyMessage(
keypair: keypair,
message: message,
signature: signature,
specVersion: specVersion,
);

Keypair crystalAlice() => RustLib.instance.api.crateApiCryptoCrystalAlice();

Expand Down
37 changes: 28 additions & 9 deletions quantus_sdk/lib/src/rust/frb_generated.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,18 @@ abstract class RustLibApi extends BaseApi {

void crateApiCryptoSetDefaultSs58Prefix({required int prefix});

Uint8List crateApiCryptoSignMessage({required Keypair keypair, required List<int> message, U8Array32? entropy});
Uint8List crateApiCryptoSignMessage({
required Keypair keypair,
required List<int> message,
U8Array32? entropy,
required int specVersion,
});

Uint8List crateApiCryptoSignMessageWithPubkey({
required Keypair keypair,
required List<int> message,
U8Array32? entropy,
required int specVersion,
});

BigInt crateApiCryptoSignatureBytes();
Expand All @@ -162,6 +168,7 @@ abstract class RustLibApi extends BaseApi {
required Keypair keypair,
required List<int> message,
required List<int> signature,
required int specVersion,
});

int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps});
Expand Down Expand Up @@ -772,32 +779,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
const TaskConstMeta(debugName: 'set_default_ss58_prefix', argNames: ['prefix']);

@override
Uint8List crateApiCryptoSignMessage({required Keypair keypair, required List<int> message, U8Array32? entropy}) {
Uint8List crateApiCryptoSignMessage({
required Keypair keypair,
required List<int> message,
U8Array32? entropy,
required int specVersion,
}) {
return handler.executeSync(
SyncTask(
callFfi: () {
final serializer = SseSerializer(generalizedFrbRustBinding);
sse_encode_box_autoadd_keypair(keypair, serializer);
sse_encode_list_prim_u_8_loose(message, serializer);
sse_encode_opt_u_8_array_32(entropy, serializer);
sse_encode_u_32(specVersion, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29)!;
},
codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null),
constMeta: kCrateApiCryptoSignMessageConstMeta,
argValues: [keypair, message, entropy],
argValues: [keypair, message, entropy, specVersion],
apiImpl: this,
),
);
}

TaskConstMeta get kCrateApiCryptoSignMessageConstMeta =>
const TaskConstMeta(debugName: 'sign_message', argNames: ['keypair', 'message', 'entropy']);
const TaskConstMeta(debugName: 'sign_message', argNames: ['keypair', 'message', 'entropy', 'specVersion']);

@override
Uint8List crateApiCryptoSignMessageWithPubkey({
required Keypair keypair,
required List<int> message,
U8Array32? entropy,
required int specVersion,
}) {
return handler.executeSync(
SyncTask(
Expand All @@ -806,18 +820,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_box_autoadd_keypair(keypair, serializer);
sse_encode_list_prim_u_8_loose(message, serializer);
sse_encode_opt_u_8_array_32(entropy, serializer);
sse_encode_u_32(specVersion, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!;
},
codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null),
constMeta: kCrateApiCryptoSignMessageWithPubkeyConstMeta,
argValues: [keypair, message, entropy],
argValues: [keypair, message, entropy, specVersion],
apiImpl: this,
),
);
}

TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta =>
const TaskConstMeta(debugName: 'sign_message_with_pubkey', argNames: ['keypair', 'message', 'entropy']);
TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => const TaskConstMeta(
debugName: 'sign_message_with_pubkey',
argNames: ['keypair', 'message', 'entropy', 'specVersion'],
);

@override
BigInt crateApiCryptoSignatureBytes() {
Expand Down Expand Up @@ -883,6 +900,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
required Keypair keypair,
required List<int> message,
required List<int> signature,
required int specVersion,
}) {
return handler.executeSync(
SyncTask(
Expand All @@ -891,18 +909,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
sse_encode_box_autoadd_keypair(keypair, serializer);
sse_encode_list_prim_u_8_loose(message, serializer);
sse_encode_list_prim_u_8_loose(signature, serializer);
sse_encode_u_32(specVersion, serializer);
return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!;
},
codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null),
constMeta: kCrateApiCryptoVerifyMessageConstMeta,
argValues: [keypair, message, signature],
argValues: [keypair, message, signature, specVersion],
apiImpl: this,
),
);
}

TaskConstMeta get kCrateApiCryptoVerifyMessageConstMeta =>
const TaskConstMeta(debugName: 'verify_message', argNames: ['keypair', 'message', 'signature']);
const TaskConstMeta(debugName: 'verify_message', argNames: ['keypair', 'message', 'signature', 'specVersion']);

@override
int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}) {
Expand Down
5 changes: 4 additions & 1 deletion quantus_sdk/lib/src/services/substrate_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,10 @@ class SubstrateService {
extrinsic = _encodeSignedExtrinsic(
signer: Uint8List.fromList(senderWallet.addressBytes),
method: encodedCall,
signature: _combineSignatureAndPubkey(senderWallet.sign(payload), senderWallet.publicKey),
signature: _combineSignatureAndPubkey(
senderWallet.sign(payload, specVersion: ctx.runtimeVersion.specVersion),
senderWallet.publicKey,
),
blockNumber: ctx.blockNumber,
nonce: ctx.nonce,
);
Expand Down
34 changes: 17 additions & 17 deletions quantus_sdk/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions quantus_sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ crate-type = ["cdylib", "staticlib", "rlib"]
[dependencies]
# NOTE: Quantus chain dependencies.
qp-poseidon-core = "3.1.0"
qp-rusty-crystals-dilithium = { version = "4.1.0", default-features = false }
qp-rusty-crystals-hdwallet = { version = "4.1.0" }
qp-rusty-crystals-dilithium = { version = "4.1.1", default-features = false }
qp-rusty-crystals-hdwallet = { version = "4.1.1" }

flutter_rust_bridge = "=2.12.0"
hex = "0.4.3"
Expand All @@ -23,12 +23,12 @@ quantus_ur = { git = "https://github.com/Quantus-Network/quantus_ur.git", tag =
anyhow = "1.0"
blake3 = "1.8"
qp-plonky2 = { version = "1.5.5", default-features = false, features = ["rand", "std"] }
qp-wormhole-circuit = { version = "4.2.0", default-features = false, features = ["std"] }
qp-wormhole-prover = { version = "4.2.0", default-features = false, features = ["std"] }
qp-wormhole-aggregator = { version = "4.2.0", default-features = false, features = ["std"] }
qp-wormhole-inputs = { version = "4.2.0", default-features = false, features = ["std"] }
qp-zk-circuits-common = { version = "4.2.0", default-features = false, features = ["std"] }
qp-wormhole-circuit-builder = { version = "4.2.0" }
qp-wormhole-circuit = { version = "4.3.0", default-features = false, features = ["std"] }
qp-wormhole-prover = { version = "4.3.0", default-features = false, features = ["std"] }
qp-wormhole-aggregator = { version = "4.3.0", default-features = false, features = ["std"] }
qp-wormhole-inputs = { version = "4.3.0", default-features = false, features = ["std"] }
qp-zk-circuits-common = { version = "4.3.0", default-features = false, features = ["std"] }
qp-wormhole-circuit-builder = { version = "4.3.0" }

[dev-dependencies]
tempfile = "3"
Expand Down
Loading
Loading