polkadot-js / tools

Various cli tools for Polkadot and Substrate chains, including basic node monitoring, making API queries via a cli app and other command-line tools
Apache License 2.0
278 stars 69 forks source link

signer-cli generates bad signature #175

Open robin-crypto-com opened 3 years ago

robin-crypto-com commented 3 years ago

See my previous comment for reference: https://github.com/polkadot-js/tools/issues/174#issuecomment-704054933

After some digging, here's how the api generates a signature from an extrinsicPayload: const { signature } = extrinsicPayload.sign(myKeyringPair); which is equal to:

const extrinsicPayloadU8a = extrinsicPayload.toU8a({ method: true });
const encoded = extrinsicPayloadU8a.length > 256 ? api.registry.hash(extrinsicPayloadU8a) : extrinsicPayloadU8a;
const signatureU8a = fromKeysPair.sign(encoded, { withType: true });
const signature = u8aToHex(signatureU8a);

the signer-cli is doing it this way:

import { hexToU8a, u8aToHex, u8aConcat } from "@polkadot/util";
const payloadU8a = hexToU8a(extrinsicPayloadHex);
const signatureU8a = fromKeysPair.sign(payloadU8a);
const prefix = new Uint8Array([0]); // [0] keyring type ed25519 or [1] keyring type sr25519
const signatureU8aWithPrefix = u8aConcat(prefix, signatureU8a);
const signature = u8aToHex(signatureU8aWithPrefix);

I think it should be something like that instead:

const payloadU8a = extrinsicPayload.toU8a({ method: true });
const rawSignatureU8a = fromKeysPair.sign(payloadU8a, { withType: true });
const signatureHex = u8aToHex(rawSignatureU8a);
jacogr commented 3 years ago

Yes, certainly not sure why the cli would not use the withType, it should. (Believe this probably precedes withType). Additionally, if it doesn't apply the hashing on large payloads, it should.

Having said that, the concat is sane (although it should not be here), since it mirrors https://github.com/polkadot-js/common/blob/master/packages/keyring/src/pair/index.ts#L173-L178

pavelrib commented 3 years ago

I don't know if it's the same issue, but I'm too using an ed25519 key and I receive 1010: Invalid Transaction: Transaction has a bad signature.

Run:
polkadot-js-signer submit --account <source_account>  --ws wss://rpc.polkadot.io/  balances.transfer <dest_account> 1020000000
Payload: 0x0500c8deea5a67504d644b16bf91b733b9b89a5ae1a9a067ee9a8fe03f6e9c9e3f5202dc2ff3450108001a0000000500000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c378ac8eb5f8d4fe99e726fd5d19a2e19cf730d888a65e3ceaf25af2c01b83325d

Run:
polkadot-js-signer sign --account <source_account> --seed <source_account_key_hex> --type ed25519 0x0500c8deea5a67504d644b16bf91b733b9b89a5ae1a9a067ee9a8fe03f6e9c9e3f5202dc2ff3450108001a0000000500000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c378ac8eb5f8d4fe99e726fd5d19a2e19cf730d888a65e3ceaf25af2c01b83325d
Signature: 0x00a04fb36f3d69bf761d7a68f4387b13329e215422ac6e7c68697b3cdc2eac8f2c9d701a3c96e8d8068d8dbaff733ff93b6ede2f7e5e1733761969e342a1e99a0a

Adding signature:
Signature> 0x00a04fb36f3d69bf761d7a68f4387b13329e215422ac6e7c68697b3cdc2eac8f2c9d701a3c96e8d8068d8dbaff733ff93b6ede2f7e5e1733761969e342a1e99a0a

Was it tested recently with this type of key?

jacogr commented 3 years ago

Which chain is this? Polkadot/Kusama/other?

EDIT: Re-reading, that was a very stupid question, everybody can see the --ws param.

pavelrib commented 3 years ago

Polkadot

jacogr commented 3 years ago

Very strange, exact same does work for me on both sr25519 and sr25519 keys. (2 different accounts). How long was the delay between creation of the payload and submission of the signature? (Just wondering if the 5 minute mortal era comes into play here)

pavelrib commented 3 years ago

No, I send it almost instantaneously.
If I sign the payload with my local eddsa signing script and paste the output into the submit script, I get this error:

Signature> 0x939bd75a06d1d2e2964c39a971c40c6d29a537aa3553c4c96f23bef23039c70243554cd5bc18d66cbb019bb7571ecea6552c00ab24ebe5b3f3caf55afe942305
REGISTRY: Error: Unable to create Enum via index 147, in Ed25519, Sr25519, Ecdsa
    at assert (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/util/assert.js:34:11)
    at createFromValue (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:45:20)
    at decodeFromValue (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:74:12)
    at decodeFromString (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:69:5)
    at decodeFromValue (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:78:12)
    at decodeEnum (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:98:10)
    at new Enum (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:145:21)
    at new <anonymous> (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/codec/Enum.js:157:9)
    at new Type (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/create/registry.js:340:16)
    at new Type (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/create/registry.js:340:16)
Error: createType(ExtrinsicSignature):: Unable to create Enum via index 147, in Ed25519, Sr25519, Ecdsa
    at createTypeUnsafe (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/create/createType.js:72:11)
    at createType (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/create/createType.js:84:10)
    at TypeRegistry.createType (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/create/registry.js:297:39)
    at Map.addSignature (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/extrinsic/v4/ExtrinsicSignature.js:151:93)
    at Map.addSignature (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/extrinsic/v4/Extrinsic.js:94:20)
    at Submittable.addSignature (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/types/extrinsic/Extrinsic.js:241:15)
    at Submittable.value (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/api/submittable/createClass.js:220:17)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async MergeMapSubscriber.project (/Users/pavelr/.nvm/versions/node/v14.15.1/lib/node_modules/@polkadot/signer-cli/node_modules/@polkadot/api/submittable/createClass.js:148:26)

It should be a legitimate ed25519 signature, any reason why this happens?

jacogr commented 3 years ago

The above signature is not correct. For the Substrate signatures (MultiSiginature type) it is always 65/66 bytes in length. The first byte is always 00, 01 or 02, (00 = ed25519, 01 = sr25519, 02 = ecdsa), the reaming bytes contain the actual signature data. (64 following for sr/ed, 65 for ecdsa)

pavelrib commented 3 years ago

I see.. missed it. The transaction is submitted ok now, thanks! Not sure then why it doesn't work with the signer-cli...

joelamouche commented 3 years ago

Is this still an issue @pavelrib ?

polariseye commented 3 years ago

@pavelrib could you please tell me how to fix it? my code happed too~

import type { Signer, SignerResult } from '@polkadot/api/types';
import type { SignerPayloadRaw } from '@polkadot/types/types';
import { KeyringPair } from "@polkadot/keyring/types";
import { assert, isHex,u8aToHex } from '@polkadot/util';
import { blake2AsHex } from '@polkadot/util-crypto';
import { ApiPromise,WsProvider } from "@polkadot/api";
import { cryptoWaitReady, mnemonicGenerate } from '@polkadot/util-crypto';
import { Keyring } from "@polkadot/keyring";
import type { SignerOptions } from '@polkadot/api/submittable/types';

export default class RawSigner implements Signer {
    alice:KeyringPair ;
    constructor(alice:KeyringPair){
        this.alice=alice;
    }
    public async signRaw ({ data }: SignerPayloadRaw): Promise<SignerResult> {
        const hashed = (data.length > (256 + 1) * 2)
        ? blake2AsHex(data)
        : data;
        console.log(`unsigned:${data}`)

        try{
            let sigVal = this.alice.sign(hashed);
            return {id:1,signature:u8aToHex(sigVal)};
        }catch(err){
            console.log("error:",err)
            throw err;
        }
  }
}

async function main() {
    await cryptoWaitReady();

    const wsProvider = new WsProvider('wss://westend-rpc.polkadot.io');
    const api = await ApiPromise.create({ provider: wsProvider });

    let keyring= new Keyring({
        type:"sr25519",
    });

    const Alice = keyring.addFromUri("//Alice");
    let from = Alice.address;
    let to = "5H2Dq1m8Cg7qsMaADCxnPDf3mk3CePDiVdVF6nyNeRbKWjc3";
    let amount = BigInt(12345);

    const options: Partial<SignerOptions> = { signer: new RawSigner(Alice) };

    const accountInfo = await api.query.system.account(from);
    let signedBlock = await api.rpc.chain.getBlock();

    options.nonce = accountInfo.nonce;
    options.blockHash = signedBlock.hash;    
    options.era = 0;

    const tx = api.tx.balances.transfer(to, amount);
    let signResult = await tx.signAndSend(from,options);
    console.log(`tx:${signResult.hash}`)

    //let remoteHash = await signResult.send()
    //console.log(`remote tx:${remoteHash}`)
}

main().catch(console.log).finally(()=>{ console.log("finished") })
ahmetk77 commented 2 years ago

How do I disable or code signing? keeps giving error while signing transaction.

jacogr commented 2 years ago

@polariseye Your sign is incorrect - you need to pass the flag to add the identification byte. (See the sign options on the keyring)