I'm trying to figure out why my pallet UI produces wrong signatures when using Polkadot.js extension. UI is similar to https://polkadot.js.org/apps/#/signing: it calculates the signature over data and also sends the (data, account public key and calculated signature) as a signed transaction to a backend. The extrinsic at the backend uses: sp_io::crypto::sr25519_verify function to validate the signature over the data and ensure_signed to validate the signature over the transaction.
sp_io::crypto::sr25519_verify works fine when I use the data signed by default accounts like Alice, Bob etc or with accounts created from browser but fails with accounts created in Polkadot.js extension.
I've also tried to figure out how the page https://polkadot.js.org/apps/#/signing works, but I failed to validate any of signatures created by the https://polkadot.js.org/apps/#/signing page with sp_io::crypto::sr25519_verify.
Could you explain?
- A. Why I have the problem with data signed by the extension but don't have a problem with local accounts?
- B. Why signature that is generated by https://polkadot.js.org/apps/#/signing doesn't pass sp_io::crypto::sr25519_verify checks?
Here are examples: 1A. GOOD
fn right_polkadot_js_local_referee_sign() {
new_test_ext().execute_with(|| {
//--------
let letter_id: u32 = 0;
let ask_price_u128: u128 = 1000000000000000;
let referee_id_bytes: [u8; 32] = [
212, 53, 147, 199, 21, 253, 211, 28,
97, 20, 26, 189, 4, 169, 159, 214,
130, 44, 133, 88, 133, 76, 205, 227,
154, 86, 132, 231, 165, 109, 162, 125
];
let worker_id_bytes: [u8; 32] = [
142, 175, 4, 21, 22, 135, 115, 99,
38, 201, 254, 161, 126, 37, 252, 82,
135, 97, 54, 147, 201, 18, 144, 156,
178, 38, 170, 71, 148, 242, 106, 72
];
let referee_sign_bytes: [u8; 64] = [
182, 73, 190, 228, 168, 27, 40, 93, 241, 1, 189,
213, 181, 17, 37, 5, 40, 185, 154, 183, 57, 178,
69, 102, 202, 243, 230, 192, 75, 40, 124, 16, 254,
95, 7, 125, 50, 157, 254, 1, 204, 32, 22, 2,
176, 45, 232, 5, 152, 71, 213, 175, 102, 235, 177,
237, 199, 213, 84, 125, 226, 142, 206, 137
];
//--------
let ask_price_bytes = &ask_price_u128.to_be_bytes();
let letter_id_bytes = &letter_id.to_be_bytes();
let mut skill_receipt_data = Vec::new();
skill_receipt_data.extend_from_slice(letter_id_bytes);
skill_receipt_data.extend_from_slice(&referee_id_bytes);
skill_receipt_data.extend_from_slice(&worker_id_bytes);
skill_receipt_data.extend_from_slice(ask_price_bytes);
assert_eq!(
LettersModule::signature_is_valid(
H512::from(referee_sign_bytes),
skill_receipt_data,
H256::from(referee_id_bytes)
),
true
);
});
}
------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
sp_io::crypto::sr25519_verify(
&Signature::from_raw(*signature.as_fixed_bytes()),
&message,
&Public::from_h256(pubkey),
)
}
2B. BAD (fails)
fn right_polkadot_js_extension_referee_sign() {
new_test_ext().execute_with(|| {
//--------
let letter_id: u32 = 23;
let ask_price_u128: u128 = 1000000000000000;
let referee_id_bytes: [u8; 32] = [
202, 112, 158, 97, 34, 240, 209, 219, 93, 46, 189, 180, 28, 113, 25, 197, 205, 6, 81,
50, 184, 168, 77, 159, 24, 205, 125, 9, 110, 129, 98, 22,
];
let worker_id_bytes: [u8; 32] = [
142, 175, 4, 21, 22, 135, 115, 99, 38, 201, 254, 161, 126, 37, 252, 82, 135, 97, 54,
147, 201, 18, 144, 156, 178, 38, 170, 71, 148, 242, 106, 72,
];
let referee_sign_bytes: [u8; 64] = [
46, 98, 203, 32, 13, 16, 69, 158, 4, 224, 203, 206, 205, 18, 44, 113, 74, 154, 131, 90,
154, 30, 71, 181, 186, 130, 120, 30, 8, 253, 177, 25, 26, 56, 200, 13, 48, 180, 5, 9,
30, 190, 171, 221, 146, 79, 231, 151, 59, 47, 1, 177, 117, 99, 119, 23, 69, 68, 27,
219, 112, 27, 245, 132,
];
//--------
let ask_price_bytes = &ask_price_u128.to_be_bytes();
let letter_id_bytes = &letter_id.to_be_bytes();
let mut skill_receipt_data = Vec::new();
skill_receipt_data.extend_from_slice(letter_id_bytes);
skill_receipt_data.extend_from_slice(&referee_id_bytes);
skill_receipt_data.extend_from_slice(&worker_id_bytes);
skill_receipt_data.extend_from_slice(ask_price_bytes);
assert_eq!(
LettersModule::signature_is_valid(
H512::from(referee_sign_bytes),
skill_receipt_data,
H256::from(referee_id_bytes)
),
true
);
});
}
------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
sp_io::crypto::sr25519_verify(
&Signature::from_raw(*signature.as_fixed_bytes()),
&message,
&Public::from_h256(pubkey),
)
}
2A. GOOD: sp_io::crypto::sr25519_verify validates signature generated by sign function from Keyring (@polkadot/keyring)
------ tests.rs ------
// //Alice
// account: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
// account_hex: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
// dataHex: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
// signatureHex: 0x803453ffa845ff410d66fadef8fcbccf5131e117f9e9dd92ff0dc6fff5c6051fda5e3a4dd291d2093172cabada00c0bbe9fb5ab481ee65cc431928cd09cd1c81
// true: signature is valid by signatureVerify from @polkadot/util-crypto
let account_bytes: [u8; 32] = [212,53,147,199,21,253,211,28,97,20,26,189,4,169,159,214,130,44,133,88,133,76,205,227,154,86,132,231,165,109,162,125];
let data_bytes: [u8; 32] = [142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72];
let signature_bytes: [u8; 64] = [128,52,83,255,168,69,255,65,13,102,250,222,248,252,188,207,81,49,225,23,249,233,221,146,255,13,198,255,245,198,5,31,218,94,58,77,210,145,210,9,49,114,202,186,218,0,192,187,233,251,90,180,129,238,101,204,67,25,40,205,9,205,28,129];
let mut data_to_sign = Vec::new();
data_to_sign.extend_from_slice(&data_bytes);
assert_eq!(
LettersModule::signature_is_valid(
H512::from(signature_bytes),
data_to_sign,
H256::from(account_bytes)
),
true
);
------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
sp_io::crypto::sr25519_verify(
&Signature::from_raw(*signature.as_fixed_bytes()),
&message,
&Public::from_h256(pubkey),
)
}
2B. BAD: sp_io::crypto::sr25519_verify marks signature as INVALID. The signature was generated by https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/signing
// --------- for Rust tests ------------
// Test-local (extension) (https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Frpc.polkadot.io#/signing)
// account: 15aS8Pih7iv6ZUVBL4AXkhihuZEaMbXdXe6CpN1MDE8LXGkm
// account_hex: 0xca709e6122f0d1db5d2ebdb41c7119c5cd065132b8a84d9f18cd7d096e816216
// dataHex: 0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
// signatureHex: 0xc043d89ebc0046edda36715cc60b5bc1b433d95aa445ae40b3d7264802b5976582c47d2750ec18ae16194b8d75e46d3f3fa6028625717aa6c86741864dad0086
// true: signature is valid by signatureVerify from @polkadot/util-crypto
let account_bytes: [u8; 32] = [202,112,158,97,34,240,209,219,93,46,189,180,28,113,25,197,205,6,81,50,184,168,77,159,24,205,125,9,110,129,98,22];
let data_bytes: [u8; 32] = [142,175,4,21,22,135,115,99,38,201,254,161,126,37,252,82,135,97,54,147,201,18,144,156,178,38,170,71,148,242,106,72];
let signature_bytes: [u8; 64] = [192,67,216,158,188,0,70,237,218,54,113,92,198,11,91,193,180,51,217,90,164,69,174,64,179,215,38,72,2,181,151,101,130,196,125,39,80,236,24,174,22,25,75,141,117,228,109,63,63,166,2,134,37,113,122,166,200,103,65,134,77,173,0,134];
let mut data_to_sign = Vec::new();
data_to_sign.extend_from_slice(&data_bytes);
assert_eq!(
LettersModule::signature_is_valid(
H512::from(signature_bytes),
data_to_sign,
H256::from(account_bytes)
),
true
);
------ lib.rs ------
fn signature_is_valid(signature: H512, message: Vec<u8>, pubkey: H256) -> bool {
sp_io::crypto::sr25519_verify(
&Signature::from_raw(*signature.as_fixed_bytes()),
&message,
&Public::from_h256(pubkey),
)
}