Last active
May 24, 2023 02:19
-
-
Save nuke-web3/f8a9e80e6b2f9429a7fe7c1e0173c484 to your computer and use it in GitHub Desktop.
PBA Signature Demos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
}, | |
"tags": [] | |
}, | |
"source": [ | |
"# Signing Demo for Web3 Engineers" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"tags": [] | |
}, | |
"source": [ | |
"## \\[Note on use\\] Reset, keeping build artifacts\n", | |
"\n", | |
"Run the below cell or commands in a shell to \"reset\" the kernel, keeping build artifacts:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"/tmp/.tmp3GYuJy\"\n" | |
] | |
}, | |
"execution_count": 3, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
":clear // Clear all state, **keeping compilation cache**, use this over a kernel restart when possible. You will need to re-run the :deps to have them loaded into state. \n", | |
":last_compile_dir // Show where the target is for cargo for this kernel, in case you want to recover these" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Set the env var `EVCXR_TMPDIR=<the output of :last_compile_dir>` to get the REPL to use this dir.\n", | |
"\n", | |
"```sh\n", | |
"# Launching jupyter-lab\n", | |
"EVCXR_TMPDIR=<dir from :last_compile_dir> jupyter-lab\n", | |
"\n", | |
"# Using Evcxr in a shell\n", | |
"EVCXR_TMPDIR=<dir from :last_compile_dir> evcxr\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
}, | |
"tags": [] | |
}, | |
"source": [ | |
"## Notebook Kernel & REPL Environment Setup\n", | |
"\n", | |
"The below should be run at kernel startup before you start.\n", | |
"All dependencies that we need to build _before_ anything else in this notebook will work.\n", | |
"\n", | |
"Instead of rebuilding see the `Reset, keeping build artifacts` section to clear REPL state only.\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
":dep sp-core = { version = \"7.0.0\", git = \"https://github.com/paritytech/substrate.git\", branch = \"polkadot-v0.9.36\" }\n", | |
"\n", | |
"// Loading & building dependencies crates here takes *a while*! \n", | |
"// Run this while you move on to the readings below.\n", | |
"// NOTE: A kernel restart removes all target artifacts!\n", | |
"// ONLY restart only if explicitly needed." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"slideshow": { | |
"slide_type": "slide" | |
}, | |
"tags": [] | |
}, | |
"source": [ | |
"## Digital Signatures\n", | |
"\n", | |
"Here we demonstrate a few parts of the [Substrate Primitives (`sp_core`)](https://paritytech.github.io/substrate/master/sp_core/index.html) library for interacting with keys and signatures." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"use sp_core::{\n", | |
"\tblake2_256,\n", | |
"\tcrypto::{Derive, Ss58Codec, Ss58AddressFormatRegistry},\n", | |
"\tDeriveJunction,\n", | |
"\thexdisplay::HexDisplay,\n", | |
"\tPair as _,\n", | |
"\tsr25519::{Pair, Public},\n", | |
"};" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Key Generation" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Generate a secret key.\n", | |
"let (pair, mnemonic, _) = Pair::generate_with_phrase(None);" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"hawk false thought wife devote close night pizza cabin novel across melt\"" | |
] | |
}, | |
"execution_count": 7, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Reveal your Secret Seed Phrase\n", | |
"mnemonic" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Derive the public key.\n", | |
"let pk = pair.public();" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"[38, 16, 19, 15, 113, 123, 87, 121, 148, 236, 175, 26, 71, 48, 211, 188, 209, 16, 136, 10, 144, 136, 122, 166, 43, 172, 47, 201, 201, 42, 162, 38]" | |
] | |
}, | |
"execution_count": 9, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Print public key as raw bytes\n", | |
"pk.0" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"2610130f717b577994ecaf1a4730d3bcd110880a90887aa62bac2fc9c92aa226" | |
] | |
}, | |
"execution_count": 10, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Print public key hex encoded \n", | |
"<HexDisplay>::from(&pk.0)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Signatures" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Sign a message.\n", | |
"let msg = b\"Welcome to Polkadot Blockchain Academy!\";\n", | |
"let sig = pair.sign(&msg[..]);" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"Welcome to Polkadot Blockchain Academy!\"" | |
] | |
}, | |
"execution_count": 12, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Message:\n", | |
"std::str::from_utf8(&msg[..]).unwrap()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"c6ff39639ec55a975391e222254bac992b2d95cc5393c75444c45bdcb812af04468b549b81c1edbb9c4b06c683353dd23636e2199096553ddb40573c8cd2508d" | |
] | |
}, | |
"execution_count": 13, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Signature, hex encoded:\n", | |
"&sig" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Verify the sig. `assert!` will not panic here with a valid sig\n", | |
"assert!(Pair::verify(&sig, &msg[..], &pk));" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Alter the message. `assert!` will panic on msg, here we assert it's fails.\n", | |
"let tampered = b\"Welcome-to-Polkadot-Blockchain-Academy!\";\n", | |
"assert!(!Pair::verify(&sig, &tampered[..], &pk));" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Signing & Hashes\n", | |
"\n", | |
"The signature process can be resource intensive with large data. We can use a *cryptographic hash function* of the data, which is generally a fixed small size and then sign that hash. This signature, the hashing protocol, and the original data is then used to be as valid to us as the signature on the original data without as much resource use. \n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Signing a message hash example, instead of the full msg.\n", | |
"let long_msg =\n", | |
"\tb\"The term \\\"Web3\\\" (AKA Web 3.0) was coined in 2014 by Ethereum co-founder Gavin Wood, is an idea for a new iteration of the World Wide Web which incorporates concepts such as decentralization, blockchain technologies, and token-based economics.\";\n", | |
"let lm_hash = blake2_256(&long_msg[..]);\n", | |
"\n", | |
"let sig_lm_hash = pair.sign(&lm_hash[..]);" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"652c97cbbae2caeb52556680c55096c6a9e9aca8318461f1cb4e74019833ed32" | |
] | |
}, | |
"execution_count": 17, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Hex print hash & signature\n", | |
"<HexDisplay>::from(&lm_hash)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Verify the signature from the same hash of the original message.\n", | |
"assert!(Pair::verify(&sig_lm_hash, blake2_256(&long_msg[..]), &pk));" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Hard Derivation" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 19, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Derive new key pairs from the original mnemonic using `//polkadot`.\n", | |
"let pair_polkadot = Pair::from_string(&format!(\"{}//polkadot\", &mnemonic), None);\n", | |
"let pk_polkadot = pair_polkadot.unwrap().public();" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 20, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"8a780f44284e9b2137ffa1ab94b05ba91cb903976995298e7aab04ed54219956" | |
] | |
}, | |
"execution_count": 20, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Polkadot Public Key, Hex\n", | |
"<HexDisplay>::from(&pk_polkadot.0)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 21, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"148ZGrDNoUxCXk1SzL8SspRsxqW5Xtb7W9AxkhdhTFgQ7QEy\"" | |
] | |
}, | |
"execution_count": 21, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Polkadot Public Key, SS58 for Polkadot\n", | |
"&pk_polkadot.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 22, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Derive new key pairs from the original mnemonic using `//kusama`.\n", | |
"let pair_kusama = Pair::from_string(&format!(\"{}//kusama\", &mnemonic), None);\n", | |
"let pk_kusama = pair_kusama.unwrap().public();" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 23, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"fc760ea0f7e45b2c4e1b0624cf953b8de471811a57e73db7514e387e6c78e573" | |
] | |
}, | |
"execution_count": 23, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Kusama Public Key, Hex\n", | |
"<HexDisplay>::from(&pk_kusama.0)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 24, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"JHLewmVTEEzXwARwCKyFh4syXK98SFUPNQg6YDorRMP3ELx\"" | |
] | |
}, | |
"execution_count": 24, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Kusama Public Key, SS58 for Polkadot\n", | |
"&pk_kusama.to_ss58check_with_version(Ss58AddressFormatRegistry::KusamaAccount.into())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## SS58 Tool\n", | |
"\n", | |
"https://polkadot.subscan.io/tools/ss58_transform\n", | |
"\n", | |
"This handy tool can be used to cross check a raw hex encoded pubkey, and see all SS58 variants for them." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Soft Derivation" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 25, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Derive a soft path on the Polkadot key.\n", | |
"let pair_polkadot_zero = Pair::from_string(&format!(\"{}//polkadot/0\", &mnemonic), None);\n", | |
"let pk0_from_secret = pair_polkadot_zero.unwrap().public();" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 26, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"123MSN6RKzp8Ha16LmajgeUKJ58YAhPV2UBhAhR5Yswjv2zg\"" | |
] | |
}, | |
"execution_count": 26, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Polkadot Soft-Derived Public Key (from secret)\n", | |
"&pk0_from_secret.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 27, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"// Derive a soft path only using the _public key_ of the //polkadot pair.\n", | |
"let pk_polkadot: Public = Public(pk_polkadot.0);\n", | |
"let path0 = vec![DeriveJunction::soft(0u8)];\n", | |
"let pk0_from_public = pk_polkadot.derive(path0.into_iter()).unwrap();" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 28, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\"123MSN6RKzp8Ha16LmajgeUKJ58YAhPV2UBhAhR5Yswjv2zg\"" | |
] | |
}, | |
"execution_count": 28, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Polkadot Soft-Derived Public Key (from public only)\n", | |
"&pk0_from_public.to_ss58check_with_version(Ss58AddressFormatRegistry::PolkadotAccount.into())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 29, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"()" | |
] | |
}, | |
"execution_count": 29, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"// Keys are identical.\n", | |
"assert_eq!(pk0_from_secret, pk0_from_public)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Rust", | |
"language": "rust", | |
"name": "rust" | |
}, | |
"language_info": { | |
"codemirror_mode": "rust", | |
"file_extension": ".rs", | |
"mimetype": "text/rust", | |
"name": "rust", | |
"pygment_lexer": "rust", | |
"version": "" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 4 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Subkey Signature and HDKD (Hierarchical Deterministic Key Derivation) Demo
All the subkey examples also exist in a jupyter notebook for reference.
As an alternative, here are subkey examples to compliment/replace using the REPL.
Key Generation
Sign
Verify
Tamper with the Message
Hard Derivation
Soft Derivation from Secret
Soft Derivation from Public
Note: We use addresses here because Subkey does not derive paths from a raw public key (AFAIK).
subkey inspect 12mzv68gS8Zu2iEdt4Ktkt48JZSKyFSkAVjvtgYhoa42NLNa/0 Public Key URI `12mzv68gS8Zu2iEdt4Ktkt48JZSKyFSkAVjvtgYhoa42NLNa/0` is account: Network ID/Version: polkadot Public key (hex): 0x40f22875159420aca51178d1baf2912c18dcb83737dd7bd39dc6743da326dd1c Account ID: 0x40f22875159420aca51178d1baf2912c18dcb83737dd7bd39dc6743da326dd1c Public key (SS58): 12UA12xuDnEkEsEDrR4T4Cf3S1Hyi2C7B6hJW8LTkcsZy8BX SS58 Address: 12UA12xuDnEkEsEDrR4T4Cf3S1Hyi2C7B6hJW8LTkcsZy8BX
subkey inspect 12mzv68gS8Zu2iEdt4Ktkt48JZSKyFSkAVjvtgYhoa42NLNa/1 Public Key URI `12mzv68gS8Zu2iEdt4Ktkt48JZSKyFSkAVjvtgYhoa42NLNa/1` is account: Network ID/Version: polkadot Public key (hex): 0xc62ec5cd7d83e1f41462d455bb47b6bad9ed5a14741a920ead8366c63746391b Account ID: 0xc62ec5cd7d83e1f41462d455bb47b6bad9ed5a14741a920ead8366c63746391b Public key (SS58): 15UrNnNSMpX49F3mWcCX7y4kMGcvnQxCabLMT3d8U5abpwr3 SS58 Address: 15UrNnNSMpX49F3mWcCX7y4kMGcvnQxCabLMT3d8U5abpwr3