Could Polkadot experts execute the code below and advise me on the questions, please?
You just need to replace the code by your own Westend accounts and nonce.
import { construct, getRegistry, methods, deriveAddress } from "@substrate/txwrapper-polkadot";
import { signatureVerify, cryptoWaitReady } from '@polkadot/util-crypto';
import { Keyring } from '@polkadot/api';
import { EXTRINSIC_VERSION } from '@polkadot/types/extrinsic/v4/Extrinsic'
await cryptoWaitReady();
console.debug('build Keyring')
const drhan_keyring = new Keyring({ type: "sr25519"});
console.debug('Some mnemonic phrase, Add an account, straight mnemonic')
const drhan_substrateKeypair = drhan_keyring.addFromUri('world dutch flash motor outdoor major axis gloom rice pledge true suit');
console.debug(`publicKey: ${drhan_substrateKeypair.publicKey}`);
console.debug('derive account address');
const drhan_address = deriveAddress(drhan_substrateKeypair.publicKey, 42);
console.debug(`address: ${drhan_address}`);
const dest = "5Da4PC8yrDnr2RcKFLFWyuZ9XNei5mPYdAvyn5aUpBGV5Ydk";
const value = 1000000000;
const nonce = "34";
console.debug('Get the meta information of the target substrate node');
const { block } = await rpcToNode('chain_getBlock');
const blockHash = await rpcToNode('chain_getBlockHash');
const genesisHash = await rpcToNode('chain_getBlockHash', [0]);
const metadataRpc = await rpcToNode('state_getMetadata');
const { specVersion, transactionVersion, specName } = await rpcToNode('state_getRuntimeVersion');
console.debug('build the registry of the target substrate node');
const registry = getRegistry({
chainName: 'Polkadot',
specName,
specVersion,
metadataRpc
});
console.debug("create our `balances.transferKeepAlive` unsigned tx");
const unsigned = methods.balances.transferKeepAlive(
{
value,
dest,
},
{
address: drhan_address,
blockHash,
blockNumber: parseInt(block.header.number, 16),
eraPeriod: 50,
genesisHash,
metadataRpc,
nonce: parseInt(nonce),
specVersion,
tip: 0,
transactionVersion,
},
{
metadataRpc,
registry,
}
);
console.debug("Construct the signing payload from an unsigned transaction");
const signingPayload = construct.signingPayload(unsigned, { registry });
console.debug(`signingPayload: ${signingPayload}`);
console.debug("Construct signature");
const { signature } = registry.createType('ExtrinsicPayload', signingPayload, {
version: EXTRINSIC_VERSION
}).sign(drhan_substrateKeypair);
console.debug(`signature: ${signature}`);
console.debug("Construct the signed transaction");
const signedTransaction = construct.signedTx(unsigned, signature, {
metadataRpc,
registry
});
console.debug("Derive the transaction hash of the signed transaction");
const hashSignedTx = construct.txHash(signedTransaction);
console.debug('check validity of signature');
const validity = signatureVerify(signingPayload, signature, drhan_address).isValid;
const response = {
"signed_tx_payload": signedTransaction,
"signed_tx_hash": hashSignedTx,
"signatureVerify - valid?" : validity
};
export function rpcToNode(
method: string,
params: any[] = []
): Promise<any> {
return fetch('https://westend-rpc.polkadot.io:443', {
body: JSON.stringify({
id: 1,
jsonrpc: '2.0',
method,
params,
}),
headers: {
'Content-Type': 'application/json',
connection: 'keep-alive',
},
method: 'POST',
})
.then((response) => response.json())
.then(({ error, result }) => {
if (error) {
throw new Error(
`${error.code} ${error.message}: ${JSON.stringify(error.data)}`
);
}
return result;
});
}
Then, I have got the result as below, for example
{
"signed_tx_payload": "0x39028400d6a55ba606625132346da20a84918fd03fc063e78beb65a708763ce212aba51e012ace240b88ccfda390bbef5547bf7bd353d77235c1055e0e58905f944b84343b63fe35ca27170fbf69596ddc4f941f6b4be25ea4cf07932591ef3512bc10828b65018800040300429f72a2498b5abaa44433210c5a5dc2d7b29946d0db1884101817871571f16302286bee",
"signed_tx_hash": "0xfbf53347d8a80dc3444bcc5527c5e0795426ac5bf26978c236a41f920143bf4c",
"signatureVerify - valid?": false
}
Then, I have submitted signed_tx_payload to Westend tesetnet as below
const wsProvider = new WsProvider('wss://westend-rpc.polkadot.io');
const api = await ApiPromise.create({ provider: wsProvider });
const signed_tx_payload = "0x39028400d6a55ba606625132346da20a84918fd03fc063e78beb65a708763ce212aba51e012ace240b88ccfda390bbef5547bf7bd353d77235c1055e0e58905f944b84343b63fe35ca27170fbf69596ddc4f941f6b4be25ea4cf07932591ef3512bc10828b65018800040300429f72a2498b5abaa44433210c5a5dc2d7b29946d0db1884101817871571f16302286bee";
const parsedTransaction = api.tx(signed_tx_payload);
const TxHash = await api.rpc.author.submitExtrinsic(parsedTransaction);
console.log(`tx hash: ${TxHash.toString()}`);
Then the message has been returned from Westend
2023-05-10 11:29:29 API/INIT: RPC methods not decorated: mmr_root, mmr_verifyProof, mmr_verifyProofStateless
2023-05-10 11:29:29 API/INIT: westend/9420: Not decorating runtime apis without matching versions: ParachainHost/4 (1/2/3 known), TransactionPaymentApi/4 (1/2/3 known)
tx hash: 0xfbf53347d8a80dc3444bcc5527c5e0795426ac5bf26978c236a41f920143bf4c
I thought that the signed transaction offline was successfully accepted to Westend testnet because I got the same hash of the transantion from Westend. But I have several questions below:
Although this extrinsic was accepted to Westend testnet, it have not been commited to a block as I can't confirm it on westend.subscan.io and the nonce has not been increased. Why this extrinsic has not been commited although I got the same hash value from Westend ?
When creating a raw unsigned extrinsic (transaction) object above, I used rpcToNode function which is introduced in here. If there exists a better way, could you advise me, please?
As you see above, the function signatureVerify returned false strangely. Really I do not make sense of this result. Could you advise me whether I passed the wrong arguments, please?
I have also tested verification with subkey as below
subkey verify --hex --message "signingPayload" "signature" "address(ss58 format)"
where signingPayload and signature can be obtained from the code above
$subkey verify --hex --message 0x9c040300429f72a2498b5abaa44433210c5a5dc2d7b29946d0db1884101817871571f16302286bee65018800cc24000015000000e143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423edfdac6269bf6f42a3dfde7683ca419c866b1d79b0449210345cbcea5b538f252 \
0x012ace240b88ccfda390bbef5547bf7bd353d77235c1055e0e58905f944b84343b63fe35ca27170fbf69596ddc4f941f6b4be25ea4cf07932591ef3512bc10828b \
5Gv9E4mSjXJsicL4QAuRe8zq3MfGeTGp1cVmB24aRhSgrKqs
then, I got an error
Error: SignatureFormatInvalid
I would be very grateful if anyone could advise me on the questions above.
Thanks