[{"data":1,"prerenderedAt":570},["ShallowReactive",2],{"navigation_docs":3,"-math-sr25519":48,"-math-sr25519-surround":567},[4,24],{"title":5,"path":6,"stem":7,"children":8,"icon":23},"Guide","\u002Fguide","1.guide\u002F01.index",[9,11,15,19],{"title":10,"path":6,"stem":7},"Getting started",{"title":12,"path":13,"stem":14},"CLI","\u002Fguide\u002Fcli","1.guide\u002F02.cli",{"title":16,"path":17,"stem":18},"Agents","\u002Fguide\u002Fagents","1.guide\u002F03.agents",{"title":20,"path":21,"stem":22},"Playground","\u002Fguide\u002Fplayground","1.guide\u002F04.playground","i-lucide-book-open",{"title":25,"path":26,"stem":27,"children":28,"icon":47},"Math","\u002Fmath","2.math\u002F00.index",[29,31,35,39,43],{"title":30,"path":26,"stem":27},"Overview",{"title":32,"path":33,"stem":34},"Curves and points","\u002Fmath\u002Fcurves","2.math\u002F01.curves",{"title":36,"path":37,"stem":38},"Orders and logs","\u002Fmath\u002Fgroups","2.math\u002F02.groups",{"title":40,"path":41,"stem":42},"secp256k1","\u002Fmath\u002Fsecp256k1","2.math\u002F03.secp256k1",{"title":44,"path":45,"stem":46},"sr25519","\u002Fmath\u002Fsr25519","2.math\u002F04.sr25519","i-lucide-sigma",{"id":49,"title":44,"body":50,"description":559,"extension":560,"links":561,"meta":562,"navigation":100,"path":45,"seo":564,"stem":46,"__hash__":566},"docs\u002F2.math\u002F04.sr25519.md",{"type":51,"value":52,"toc":551},"minimark",[53,58,62,190,201,211,215,296,303,320,329,333,403,414,426,430,470,473,476,480,501,504,508,533,541,547],[54,55,57],"h2",{"id":56},"your-polkadot-account-isnt-on-secp256k1","Your Polkadot account isn't on secp256k1",[59,60,61],"p",{},"Polkadot wallets make new accounts on sr25519. That's Schnorr signatures over ristretto255, a group built on edwards25519. Same words, other curve, other address. The fun one, too.",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-ts shiki shiki-themes compressions compressions compressions","import { deriveHard, expandSeed, getPublicKey } from \"@agntn\u002Fcurves\u002Fsr25519\";\n\nconst root = expandSeed(\"fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e\");\ngetPublicKey(root); \u002F\u002F \"46ebddef8cd9bb16…b47a\"\n\nconst alice = deriveHard(root, \"14416c696365\".padEnd(64, \"0\"));\ngetPublicKey(alice); \u002F\u002F \"d43593c715fdd31c…a27d\"\n","ts","",[70,71,72,95,102,127,140,145,179],"code",{"__ignoreMap":68},[73,74,77,81,85,88,92],"span",{"class":75,"line":76},"line",1,[73,78,80],{"class":79},"skH_V","import",[73,82,84],{"class":83},"s38Sx"," { deriveHard, expandSeed, getPublicKey } ",[73,86,87],{"class":79},"from",[73,89,91],{"class":90},"shU9J"," \"@agntn\u002Fcurves\u002Fsr25519\"",[73,93,94],{"class":83},";\n",[73,96,98],{"class":75,"line":97},2,[73,99,101],{"emptyLinePlaceholder":100},true,"\n",[73,103,105,108,111,114,118,121,124],{"class":75,"line":104},3,[73,106,107],{"class":79},"const",[73,109,110],{"class":83}," root ",[73,112,113],{"class":79},"=",[73,115,117],{"class":116},"sK71F"," expandSeed",[73,119,120],{"class":83},"(",[73,122,123],{"class":90},"\"fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e\"",[73,125,126],{"class":83},");\n",[73,128,130,133,136],{"class":75,"line":129},4,[73,131,132],{"class":116},"getPublicKey",[73,134,135],{"class":83},"(root); ",[73,137,139],{"class":138},"scIB-","\u002F\u002F \"46ebddef8cd9bb16…b47a\"\n",[73,141,143],{"class":75,"line":142},5,[73,144,101],{"emptyLinePlaceholder":100},[73,146,148,150,153,155,158,161,164,167,170,173,176],{"class":75,"line":147},6,[73,149,107],{"class":79},[73,151,152],{"class":83}," alice ",[73,154,113],{"class":79},[73,156,157],{"class":116}," deriveHard",[73,159,160],{"class":83},"(root, ",[73,162,163],{"class":90},"\"14416c696365\"",[73,165,166],{"class":83},".",[73,168,169],{"class":116},"padEnd",[73,171,172],{"class":83},"(64, ",[73,174,175],{"class":90},"\"0\"",[73,177,178],{"class":83},"));\n",[73,180,182,184,187],{"class":75,"line":181},7,[73,183,132],{"class":116},[73,185,186],{"class":83},"(alice); ",[73,188,189],{"class":138},"\u002F\u002F \"d43593c715fdd31c…a27d\"\n",[59,191,192,193,196,197,200],{},"Seen ",[70,194,195],{},"d43593c7…"," before? It's ",[70,198,199],{},"\u002F\u002FAlice",", the account every Substrate dev chain funds. The seed is the mini secret of the dev phrase. Turning a phrase into that seed happens elsewhere, not here.",[59,202,203,204,207,208,166],{},"A seed is 32 bytes. ",[70,205,206],{},"expandSeed"," turns it into a 64-byte secret the way Substrate does: the key as schnorrkel writes it for ed25519, then a nonce. Everything is hex without ",[70,209,210],{},"0x",[54,212,214],{"id":213},"sign-twice-get-two-signatures","Sign twice, get two signatures",[63,216,218],{"className":65,"code":217,"language":67,"meta":68,"style":68},"import { sign, verify } from \"@agntn\u002Fcurves\u002Fsr25519\";\n\nconst message = new TextEncoder().encode(\"hello\");\nconst signature = sign(alice, message);\nverify(signature, message, getPublicKey(alice)); \u002F\u002F true\n",[70,219,220,233,237,265,280],{"__ignoreMap":68},[73,221,222,224,227,229,231],{"class":75,"line":76},[73,223,80],{"class":79},[73,225,226],{"class":83}," { sign, verify } ",[73,228,87],{"class":79},[73,230,91],{"class":90},[73,232,94],{"class":83},[73,234,235],{"class":75,"line":97},[73,236,101],{"emptyLinePlaceholder":100},[73,238,239,241,244,246,249,252,255,258,260,263],{"class":75,"line":104},[73,240,107],{"class":79},[73,242,243],{"class":83}," message ",[73,245,113],{"class":79},[73,247,248],{"class":79}," new",[73,250,251],{"class":116}," TextEncoder",[73,253,254],{"class":83},"().",[73,256,257],{"class":116},"encode",[73,259,120],{"class":83},[73,261,262],{"class":90},"\"hello\"",[73,264,126],{"class":83},[73,266,267,269,272,274,277],{"class":75,"line":129},[73,268,107],{"class":79},[73,270,271],{"class":83}," signature ",[73,273,113],{"class":79},[73,275,276],{"class":116}," sign",[73,278,279],{"class":83},"(alice, message);\n",[73,281,282,285,288,290,293],{"class":75,"line":142},[73,283,284],{"class":116},"verify",[73,286,287],{"class":83},"(signature, message, ",[73,289,132],{"class":116},[73,291,292],{"class":83},"(alice)); ",[73,294,295],{"class":138},"\u002F\u002F true\n",[59,297,298,299,302],{},"Sign the same message again and the bytes change. Both still verify. The nonce mixes the secret with fresh random bytes, as schnorrkel does. Writing a test? Pass ",[70,300,301],{},"{ random }"," with 64 hex digits and the signature holds still.",[59,304,305,306,309,310,313,314,317,318,166],{},"Signatures go under the ",[70,307,308],{},"substrate"," signing context, the one Polkadot signs with. Some other app uses its own? Give ",[70,311,312],{},"{ context }"," to both ",[70,315,316],{},"sign"," and ",[70,319,284],{},[59,321,322,324,325,328],{},[70,323,284],{}," says ",[70,326,327],{},"false"," when the math fails. Also for a signature without schnorrkel's marker bit, or with s at or above l. It throws only on input of the wrong shape: a signature that isn't 128 hex digits, a public key that's no ristretto255 point.",[54,330,332],{"id":331},"hard-and-soft","Hard and soft",[334,335,336,352],"table",{},[337,338,339],"thead",{},[340,341,342,346,349],"tr",{},[343,344,345],"th",{},"Function",[343,347,348],{},"Starts from",[343,350,351],{},"Gives",[353,354,355,373,387],"tbody",{},[340,356,357,363,366],{},[358,359,360],"td",{},[70,361,362],{},"deriveHard(secret, chainCode)",[358,364,365],{},"the secret",[358,367,368,369,372],{},"a ",[70,370,371],{},"\u002F\u002Fhard"," child secret",[340,374,375,380,382],{},[358,376,377],{},[70,378,379],{},"deriveSoft(secret, chainCode)",[358,381,365],{},[358,383,368,384,372],{},[70,385,386],{},"\u002Fsoft",[340,388,389,394,397],{},[358,390,391],{},[70,392,393],{},"derivePublic(publicKey, chainCode)",[358,395,396],{},"the public key",[358,398,399,400,402],{},"the ",[70,401,386],{}," child's public key",[59,404,405,406,409,410,413],{},"The chain code is what Substrate makes of a junction: its SCALE encoding, padded to 32 bytes. ",[70,407,408],{},"Alice"," becomes ",[70,411,412],{},"14416c696365"," and zeros. A hard child needs the secret. A soft one doesn't, so a watch-only setup can follow it from the public key alone. Both ways land on the same key, the tests check that.",[59,415,416,417,420,421,423,424,166],{},"A soft child secret gets a fresh nonce too, so ",[70,418,419],{},"deriveSoft"," takes ",[70,422,301],{}," like ",[70,425,316],{},[54,427,429],{"id":428},"ristretto255-underneath","ristretto255 underneath",[63,431,433],{"className":65,"code":432,"language":67,"meta":68,"style":68},"import { addPoints, multiplyGenerator } from \"@agntn\u002Fcurves\u002Fristretto255\";\n\nmultiplyGenerator(1n); \u002F\u002F \"e2f2ae0a6abc4e71…2d76\"\n",[70,434,435,449,453],{"__ignoreMap":68},[73,436,437,439,442,444,447],{"class":75,"line":76},[73,438,80],{"class":79},[73,440,441],{"class":83}," { addPoints, multiplyGenerator } ",[73,443,87],{"class":79},[73,445,446],{"class":90}," \"@agntn\u002Fcurves\u002Fristretto255\"",[73,448,94],{"class":83},[73,450,451],{"class":75,"line":97},[73,452,101],{"emptyLinePlaceholder":100},[73,454,455,458,461,464,467],{"class":75,"line":104},[73,456,457],{"class":116},"multiplyGenerator",[73,459,460],{"class":83},"(1",[73,462,463],{"class":79},"n",[73,465,466],{"class":83},"); ",[73,468,469],{"class":138},"\u002F\u002F \"e2f2ae0a6abc4e71…2d76\"\n",[59,471,472],{},"edwards25519 has a cofactor of 8. A classic way to shoot yourself in the foot. ristretto255 from RFC 9496 hides it: one element, one 32-byte encoding, everything else refused. The identity is 32 zero bytes, and it's a valid point here.",[59,474,475],{},"Scalars are bigints from 0 to l - 1, never hex. Why? ed25519 reads bytes little-endian, so a hex scalar would leave you guessing which number it is.",[54,477,479],{"id":478},"written-from-the-specs-again","Written from the specs, again",[59,481,482,483,486,487,496,497,500],{},"ristretto255 follows RFC 9496 and passes its test vectors. Merlin and STROBE-128 run on a Keccak-f",[73,484,485],{},"1600"," written here. SHA-512 comes from ",[488,489,493],"a",{"href":490,"rel":491},"https:\u002F\u002Fhashes.agntn.dev",[492],"nofollow",[70,494,495],{},"@agntn\u002Fhashes",". The tests hold keys, signatures and both derivations to ",[70,498,499],{},"@scure\u002Fsr25519"," byte for byte, given the same random bytes.",[59,502,503],{},"About 4 ms to sign and 3 to verify. Slow next to a Rust wallet, fine for a puzzle. Not constant time either, so keep real funds out of it.",[54,505,507],{"id":506},"over-mcp","Over MCP",[59,509,510,513,514,517,518,517,520,317,522,525,526,529,530,166],{},[70,511,512],{},"curves_sr25519_compute"," does ",[70,515,516],{},"keypair",", ",[70,519,316],{},[70,521,284],{},[70,523,524],{},"derive",". A message is text unless ",[70,527,528],{},"encoding"," is ",[70,531,532],{},"hex",[63,534,539],{"className":535,"code":537,"language":538,"meta":68},[536],"language-text","{\"operation\":\"keypair\",\"secret\":\"28b0ae221c6bb06856b287f60d7ea0d98552ea5a16db16956849aa371db3eb51fd190cce74df356432b410bd64682309d6dedb27c76845daf388557cbac3ca34\",\"publicKey\":\"46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a\"}\n","text",[70,540,537],{"__ignoreMap":68},[59,542,543,544,546],{},"That's ",[70,545,516],{}," with the dev seed. Yes, the secret comes back in the answer. Dev accounts and burner keys only.",[548,549,550],"style",{},"html pre.shiki code .skH_V, html code.shiki .skH_V{--shiki-light:var(--shiki-token-keyword);--shiki-default:var(--shiki-token-keyword);--shiki-dark:var(--shiki-token-keyword)}html pre.shiki code .s38Sx, html code.shiki .s38Sx{--shiki-light:var(--ui-text-highlighted);--shiki-default:var(--ui-text-highlighted);--shiki-dark:var(--ui-text-highlighted)}html pre.shiki code .shU9J, html code.shiki .shU9J{--shiki-light:var(--shiki-token-string);--shiki-default:var(--shiki-token-string);--shiki-dark:var(--shiki-token-string)}html pre.shiki code .sK71F, html code.shiki .sK71F{--shiki-light:var(--shiki-token-function);--shiki-default:var(--shiki-token-function);--shiki-dark:var(--shiki-token-function)}html pre.shiki code .scIB-, html code.shiki .scIB-{--shiki-light:var(--shiki-token-comment);--shiki-default:var(--shiki-token-comment);--shiki-dark:var(--shiki-token-comment)}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":68,"searchDepth":97,"depth":97,"links":552},[553,554,555,556,557,558],{"id":56,"depth":97,"text":57},{"id":213,"depth":97,"text":214},{"id":331,"depth":97,"text":332},{"id":428,"depth":97,"text":429},{"id":478,"depth":97,"text":479},{"id":506,"depth":97,"text":507},"Polkadot keys and signatures. Schnorr over ristretto255 with Merlin transcripts and Substrate HDKD. Written from the specs","md",null,{"icon":563},"i-lucide-fingerprint",{"title":565,"description":559},"sr25519 keys, signatures and HDKD for Polkadot","p9WN085grl_Shdf0k4vmCLdl3TCS2_tBa64SyyXEruE",[568,561],{"title":40,"path":41,"stem":42,"description":569,"children":-1},"Points of secp256k1 as SEC1 hex and scalars mod n. Add and subtract and multiply and lift an x coordinate",1791414452869]