Skip to content

Add VerifyMultiSignature signed extension from frame-verify-signature#6253

Open
bkontur wants to merge 1 commit into
polkadot-js:masterfrom
bkontur:bko-verify-signature
Open

Add VerifyMultiSignature signed extension from frame-verify-signature#6253
bkontur wants to merge 1 commit into
polkadot-js:masterfrom
bkontur:bko-verify-signature

Conversation

@bkontur
Copy link
Copy Markdown
Contributor

@bkontur bkontur commented Apr 1, 2026

This adds native support for the VerifyMultiSignature signed extension (from substrate/frame/verify-signature) so chains using it no longer need custom type overrides in apps-config.

Relates to: https://github.com/paritytech/polkadot-sdk/blob/129a48aa9f17a3d5ccbcb8cb4167dd505165d35f/substrate/frame/verify-signature/src/extension.rs#L43

TODO

  • run locally and test with people-paseo-next - @peetzweg

This adds native support for the VerifyMultiSignature signed extension
(from substrate/frame/verify-signature) so chains using it no longer
need custom type overrides in apps-config.
@peetzweg
Copy link
Copy Markdown
Contributor

Thanks @bkontur, just gave it a try locally and yes I can use the VerifyMultisignature extension. However, as the people-paseo-next chain has more custom transaction extensions which needed to be passed in order to create a valid transaction. So we need a way in polkadotjs to dynamically determine the transaction extensions and put them in by default similar to how papi does it. 🤔

I think we can do this now as the extensions are listed in the latest metadata version?

Longer version from claude:


Tested locally on preview-people with a System.remark from //Alice. PR works as advertised —
VerifyMultiSignature is now a known signed extension, no more "unknown extension" warning for it. ✅

But a vanilla signAndSend still fails with a wasm unreachable panic in validate_transaction. Two issues, one in scope for this PR, one not:

  1. (in scope) verifySignature defaults to Signed { 0×64, 0×32 }, not Disabled.
    Signed is variant 0 of the enum, and polkadot-js's Enum codec defaults to variant 0. So every signed tx encodes a bogus Signed { sig: zeros, account:
    zero-AccountId }, which the verify-signature pallet rejects (Invalid signing address). For normal sr25519/ed25519 signing this should default to
    Disabled. As-is, every caller has to remember signAndSend(alice, { verifySignature: { Disabled: null } }, …) — easy to forget. Could the PR also set
    Disabled as the default?

  2. (out of scope, but worth flagging) The chain has 11 other unknown extensions (AsPerson, AsProofOfInkParticipant, ScoreAsParticipant, GameAsInvited,
    PeopleLiteAuth, AsMember, AsCoinage, AsResources, AuthorizeCall, RestrictOrigins, StorageWeightReclaim). polkadot-js silently drops them from the
    extrinsic and signing payload, but per metadata they need ~9 zero bytes (8× Option + 1× bool). The resulting SCALE misalignment is what causes the wasm
    panic. polkadot-api works because it reads these from metadata directly. They need their own definitions or signedExtensions user overrides — not this
    PR's job, but anyone hitting this PR will trip on it.

What got Alice's remark in-block (this PR + workarounds):

const api = await ApiPromise.create({                                                                                                                  
 provider,                                           
 signedExtensions: {
   AsPerson:                { extrinsic: { asPerson: 'Option<Null>' }, payload: {} },
   AsProofOfInkParticipant: { extrinsic: { asProofOfInkParticipant: 'Option<Null>' }, payload: {} },                                                  
   ScoreAsParticipant:      { extrinsic: { scoreAsParticipant: 'Option<Null>' }, payload: {} },                                                       
   GameAsInvited:           { extrinsic: { gameAsInvited: 'Option<Null>' }, payload: {} },                                                            
   PeopleLiteAuth:          { extrinsic: { peopleLiteAuth: 'Option<Null>' }, payload: {} },                                                           
   AsMember:                { extrinsic: { asMember: 'Option<Null>' }, payload: {} },                                                                 
   AsCoinage:               { extrinsic: { asCoinage: 'Option<Null>' }, payload: {} },                                                                
   AsResources:             { extrinsic: { asResources: 'Option<Null>' }, payload: {} },                                                              
   AuthorizeCall:           { extrinsic: {}, payload: {} },                                                                                           
   RestrictOrigins:         { extrinsic: { restrictOrigins: 'bool' }, payload: {} },
   StorageWeightReclaim:    { extrinsic: {}, payload: {} }                                                                                            
 }                                                   
});     

await tx.signAndSend(alice, { verifySignature: { Disabled: null } }, cb);
// → in-block: 0x32d442d7fd37ae45caf666e5578ed4fc2b602b9d49e4f0b3cc44e3b68ce991b3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants