[{"data":1,"prerenderedAt":1254},["ShallowReactive",2],{"post:\u002F2026-05-29-secp256k1-nums-public-keys":3},{"id":4,"title":5,"body":6,"date":1246,"description":1247,"extension":1248,"meta":1249,"navigation":238,"path":1250,"seo":1251,"stem":1252,"__hash__":1253},"content\u002F2026-05-29-secp256k1-nums-public-keys.md","secp256k1 NUMS Public Keys",{"type":7,"value":8,"toc":1244},"minimark",[9,14,18,29,38,42,72,84,101,105,113,120,127,133,142,152,158,182,747,773,1031,1041,1071,1210,1219,1223,1226,1229,1232,1240],[10,11,13],"h1",{"id":12},"what-does-nums-even-mean","What does NUMS even mean?",[15,16,17],"p",{},"NUMS stands for \"Nothing Up My Sleeve\". The name comes from the magician's gesture of rolling up their sleeves to prove there's nothing hidden in them before a trick. In cryptography we borrow the idea to talk about constants that are built in such an obvious, boring way that nobody could have rigged them.",[15,19,20,21,28],{},"If you want a great primer on the idea, Computerphile has a video on it: ",[22,23,27],"a",{"href":24,"rel":25},"https:\u002F\u002Fyoutu.be\u002FoJWwaQm-Exs",[26],"nofollow","Magic \"Nothing Up My Sleeve\" Numbers",".",[15,30,31,32,37],{},"Whenever an algorithm needs a \"random looking\" constant, the person choosing it could, in theory, pick a value that secretly gives them an advantage (a ",[22,33,36],{"href":34,"rel":35},"https:\u002F\u002Fwww.schneier.com\u002Fessays\u002Farchives\u002F2007\u002F11\u002Fdid_nsa_put_a_secret.html",[26],"backdoor",", a shortcut, a way to cheat later). A NUMS value sidesteps that suspicion by being derived from something everyone already trusts, like the digits of pi or the hash of a well known string. The construction is the proof: anyone can recompute it and see there was no room to cheat.",[10,39,41],{"id":40},"a-nums-key-on-secp256k1","A NUMS key on secp256k1",[15,43,44,49,50,54,55,58,59,62,63,66,67,28],{},[22,45,48],{"href":46,"rel":47},"https:\u002F\u002Fen.bitcoin.it\u002Fwiki\u002FSecp256k1",[26],"secp256k1"," is the elliptic curve Bitcoin uses. On that curve, a private key is just a number (technically a scalar in the range ",[51,52,53],"code",{},"1"," to ",[51,56,57],{},"n - 1",", where ",[51,60,61],{},"n"," is the order of the curve), and the matching public key is that number multiplied by a fixed point called the generator ",[51,64,65],{},"G",". The whole thing is secure because going backwards (figuring out the private key from the public key) is computationally hopeless. That's the ",[22,68,71],{"href":69,"rel":70},"https:\u002F\u002Fmath.mit.edu\u002Fclasses\u002F18.783\u002F2022\u002FLectureNotes9.pdf",[26],"discrete logarithm problem",[15,73,74,75,79,80,83],{},"A NUMS key flips the usual goal on its head. Normally you want a public key whose private key only ",[76,77,78],"strong",{},"you"," know. A NUMS key is a public key whose private key ",[76,81,82],{},"nobody"," knows, including the person who created it.",[15,85,86,87,90,91,94,95,97,98,28],{},"How do you get a point on the curve that no one has the private key for? You don't pick a number and multiply. Instead you take some unquestionable value, treat it as if it were the x coordinate of a point, and find the matching point on the curve (thanks to secp256k1's equation ",[51,88,89],{},"y² = x³ + 7",", that just means solving for ",[51,92,93],{},"y = √(x³ + 7) mod p",", when a square root exists). And since the value came from something public and arbitrary, you couldn't have worked backwards from a private key you secretly kept either. There is still some private key behind that point (every point on the curve has one), but the only way to find it is to solve that discrete logarithm problem, so your odds of landing on a matching key are about 1 in ",[51,96,61],{},", the curve order, or roughly 1 in ",[51,99,100],{},"2²⁵⁶",[10,102,104],{"id":103},"so-how-is-this-useful","So how is this useful?",[15,106,107,108,28],{},"The cleanest real world example is ",[22,109,112],{"href":110,"rel":111},"https:\u002F\u002Flearnmeabitcoin.com\u002Ftechnical\u002Fupgrades\u002Ftaproot\u002F",[26],"Taproot",[15,114,115,116,119],{},"A Taproot output can be spent two ways: the \"key path\" (a normal signature) or the \"script path\" (one of several spending conditions hidden in a tree). Sometimes you want a coin that can ",[76,117,118],{},"only"," be spent through the scripts, with the key path completely disabled.",[15,121,122],{},[123,124],"img",{"alt":125,"src":126},"Diagram of how a Taproot output is built: a script tree of leaves and branches reduces to a merkle root, which is combined with a public key to produce a tweak and a final tweaked public key.","\u002Fimages\u002Ftaproot-summary.webp",[15,128,129],{},[130,131,132],"em",{},"Taproot Summary, from learnmeabitcoin.com.",[15,134,135,136,141],{},"To do that, you need a public key that genuinely has no known private key, otherwise whoever knows that key could bypass all your carefully written scripts. So ",[22,137,140],{"href":138,"rel":139},"https:\u002F\u002Fbips.dev\u002F341\u002F",[26],"BIP-341"," specifies a NUMS point for exactly this purpose. It's built by hashing the standard generator point and lifting the result to the curve:",[143,144,149],"pre",{"className":145,"code":147,"language":148},[146],"language-text","H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)\n","text",[51,150,147],{"__ignoreMap":151},"",[15,153,154,155,157],{},"That hex value is the SHA256 of the encoded generator ",[51,156,65],{},". Anyone can recompute it in a couple of lines and confirm it wasn't cherry picked. Because it came out of a hash, no one knows (or can know) the corresponding private key, so the key path is provably dead and only the scripts can move the coins.",[15,159,160,161,166,167,170,171,173,174,177,178,181],{},"Here's the whole thing in ",[22,162,165],{"href":163,"rel":164},"https:\u002F\u002Fsagecell.sagemath.org\u002F?z=eJydk12Lm0AUhu8F_8Mhy7LaLkHHGWcs9CIaTXrRFrr0qi3LfJlIjZHRlOy_76jJZlO2FOqFqHM-3nOe12rX7k0PW95t60q4Tmn2O-j4Rs95XUM1nRZVU_W6qHSt7iGv66rtK5kdzC99Dx-aXm-0cR3XuYFOyxaR-GcILTd8p3ttOtcp4P3LEt4pxQuOxf9d-XjPUOH7rtPY6v-slKeLRb7M8nhRYLYIojQtlojkLFsGUYxDHALcgBwmgr1Rwzi5LXs1qvetuCj3_Xu4vFLf_-FPG_hsqk3V8Bo2utGG93sD7b5qelhB1cChkftda3TXaQXl3ux47zqrx2X-xXa7CzBNhI5jqstESSG4JIQHMUqI1IwGIqABSkQplUBKasRUQpISsZCIMC5ZSBOGWcQVpxTFPJI4JkRxXNqMQIdhwDgrVUgFxozHjBAcJonEVAWsLEUYKCzY3TTESvewPukWT6M5qmYDK9dZD1uZ11XZPx69wrMB3sk6827LLXpPPPW6mw822uqjN87m-3P7rKqN7nrPLi6M_QFca4b02Rq8T18_PoDgnZ56-rbJ7Dng7vYIt8c7uIX1_Pjk-f5Z47hfDZ3kNTcwGbc3g9DZoela3Sguaj1zHWPrnVG9ovgqeq4tIqW9VzRbBc1F9vfmYWpsrtQOQ5uLxh767WCpkylW04CW-fUegyNN0tySz4tkmaXpIiNkMZLPcks-HcmnRbZMkTUxYktLvhjIp2FcjOTPLTNey0M97MVYXGboY-DNgO76tBz1jIsfJb2zwN-OOePJY3PYdTb3_PF56OKPPPCmkL8xu1R7CW_Mfw8siojAKMSRUjFVHGnJpGAisWallMqIhML-E0lk_wXESUR5gEVJkRY0SmL-GzVpXyA=&lang=sage&interacts=eJyLjgUAARUAuQ==",[26],"SageMath",": derive the base point ",[51,168,169],{},"H"," from ",[51,172,65],{},", add ",[51,175,176],{},"r * G"," for a scalar ",[51,179,180],{},"r"," that itself comes from hashing a string, and you get a NUMS point that nobody holds the key for.",[143,183,187],{"className":184,"code":185,"language":186,"meta":151,"style":151},"language-python shiki shiki-themes vitesse-dark","import hashlib\nfrom sage.all import FiniteField, EllipticCurve, Integer\n\n# secp256k1 parameters\nF = FiniteField(Integer(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F))\nn = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141  # curve order\nE = EllipticCurve([F(Integer(0)), F(Integer(7))])\n\n# Original generator point G in uncompressed format\nG_DER = '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8'\n\n# Get H point by hashing G\nH = E.lift_x(F(int(hashlib.sha256(bytes.fromhex(G_DER)).hexdigest(), 16)))\nprint(\"H (NUMS base point) = \")\nprint('%x %x' % H.xy())\n\n# Generate scalar from string \"unspendable\"\nr = Integer(int(hashlib.sha256(\"unspendable\".encode()).hexdigest(), 16) % n)\nprint(\"\\nScalar r = \")\nprint(hex(r))\n\n# Get the original G point\nG = E.lift_x(F(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798))\n\n# Calculate rG\nrG = r * G\n\n# Calculate final NUMS point: H + rG\nfinal_nums = H + rG\nprint(\"\\nFinal NUMS point (H + rG) = \")\nprint('%x %x' % final_nums.xy())\n\n# NUMS = 8335b42143dd67da2ec8cb8b9108777c351b47993bba2a537a04bf72eb7396a\n","python",[51,188,189,202,233,240,247,277,294,337,342,348,369,374,380,445,464,496,501,507,563,582,598,603,609,636,641,647,664,669,675,692,710,736,741],{"__ignoreMap":151},[190,191,194,198],"span",{"class":192,"line":193},"line",1,[190,195,197],{"class":196},"s3QIE","import",[190,199,201],{"class":200},"sNpkn"," hashlib\n",[190,203,205,208,211,214,217,219,222,225,228,230],{"class":192,"line":204},2,[190,206,207],{"class":196},"from",[190,209,210],{"class":200}," sage",[190,212,28],{"class":213},"s_pn2",[190,215,216],{"class":200},"all ",[190,218,197],{"class":196},[190,220,221],{"class":200}," FiniteField",[190,223,224],{"class":213},",",[190,226,227],{"class":200}," EllipticCurve",[190,229,224],{"class":213},[190,231,232],{"class":200}," Integer\n",[190,234,236],{"class":192,"line":235},3,[190,237,239],{"emptyLinePlaceholder":238},true,"\n",[190,241,243],{"class":192,"line":242},4,[190,244,246],{"class":245},"sux-A","# secp256k1 parameters\n",[190,248,250,253,256,258,261,264,266,270,274],{"class":192,"line":249},5,[190,251,252],{"class":200},"F ",[190,254,255],{"class":213},"=",[190,257,221],{"class":200},[190,259,260],{"class":213},"(",[190,262,263],{"class":200},"Integer",[190,265,260],{"class":213},[190,267,269],{"class":268},"s_wWq","0x",[190,271,273],{"class":272},"sxA9i","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",[190,275,276],{"class":213},"))\n",[190,278,280,283,285,288,291],{"class":192,"line":279},6,[190,281,282],{"class":200},"n ",[190,284,255],{"class":213},[190,286,287],{"class":268}," 0x",[190,289,290],{"class":272},"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",[190,292,293],{"class":245},"  # curve order\n",[190,295,297,300,302,304,307,310,312,314,316,319,322,325,327,329,331,334],{"class":192,"line":296},7,[190,298,299],{"class":200},"E ",[190,301,255],{"class":213},[190,303,227],{"class":200},[190,305,306],{"class":213},"([",[190,308,309],{"class":200},"F",[190,311,260],{"class":213},[190,313,263],{"class":200},[190,315,260],{"class":213},[190,317,318],{"class":272},"0",[190,320,321],{"class":213},")),",[190,323,324],{"class":200}," F",[190,326,260],{"class":213},[190,328,263],{"class":200},[190,330,260],{"class":213},[190,332,333],{"class":272},"7",[190,335,336],{"class":213},"))])\n",[190,338,340],{"class":192,"line":339},8,[190,341,239],{"emptyLinePlaceholder":238},[190,343,345],{"class":192,"line":344},9,[190,346,347],{"class":245},"# Original generator point G in uncompressed format\n",[190,349,351,355,358,362,366],{"class":192,"line":350},10,[190,352,354],{"class":353},"sXjYR","G_DER",[190,356,357],{"class":213}," =",[190,359,361],{"class":360},"sNJcY"," '",[190,363,365],{"class":364},"s7rlk","0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",[190,367,368],{"class":360},"'\n",[190,370,372],{"class":192,"line":371},11,[190,373,239],{"emptyLinePlaceholder":238},[190,375,377],{"class":192,"line":376},12,[190,378,379],{"class":245},"# Get H point by hashing G\n",[190,381,383,386,388,391,393,396,398,400,402,406,408,411,413,416,418,421,423,426,428,430,433,436,439,442],{"class":192,"line":382},13,[190,384,385],{"class":200},"H ",[190,387,255],{"class":213},[190,389,390],{"class":200}," E",[190,392,28],{"class":213},[190,394,395],{"class":200},"lift_x",[190,397,260],{"class":213},[190,399,309],{"class":200},[190,401,260],{"class":213},[190,403,405],{"class":404},"sm68I","int",[190,407,260],{"class":213},[190,409,410],{"class":200},"hashlib",[190,412,28],{"class":213},[190,414,415],{"class":200},"sha256",[190,417,260],{"class":213},[190,419,420],{"class":404},"bytes",[190,422,28],{"class":213},[190,424,425],{"class":200},"fromhex",[190,427,260],{"class":213},[190,429,354],{"class":353},[190,431,432],{"class":213},")).",[190,434,435],{"class":200},"hexdigest",[190,437,438],{"class":213},"(),",[190,440,441],{"class":272}," 16",[190,443,444],{"class":213},")))\n",[190,446,448,451,453,456,459,461],{"class":192,"line":447},14,[190,449,450],{"class":404},"print",[190,452,260],{"class":213},[190,454,455],{"class":360},"\"",[190,457,458],{"class":364},"H (NUMS base point) = ",[190,460,455],{"class":360},[190,462,463],{"class":213},")\n",[190,465,467,469,471,474,477,480,482,485,488,490,493],{"class":192,"line":466},15,[190,468,450],{"class":404},[190,470,260],{"class":213},[190,472,473],{"class":360},"'",[190,475,476],{"class":353},"%x",[190,478,479],{"class":353}," %x",[190,481,473],{"class":360},[190,483,484],{"class":268}," %",[190,486,487],{"class":200}," H",[190,489,28],{"class":213},[190,491,492],{"class":200},"xy",[190,494,495],{"class":213},"())\n",[190,497,499],{"class":192,"line":498},16,[190,500,239],{"emptyLinePlaceholder":238},[190,502,504],{"class":192,"line":503},17,[190,505,506],{"class":245},"# Generate scalar from string \"unspendable\"\n",[190,508,510,513,515,518,520,522,524,526,528,530,532,534,537,539,541,544,547,549,551,553,556,558,561],{"class":192,"line":509},18,[190,511,512],{"class":200},"r ",[190,514,255],{"class":213},[190,516,517],{"class":200}," Integer",[190,519,260],{"class":213},[190,521,405],{"class":404},[190,523,260],{"class":213},[190,525,410],{"class":200},[190,527,28],{"class":213},[190,529,415],{"class":200},[190,531,260],{"class":213},[190,533,455],{"class":360},[190,535,536],{"class":364},"unspendable",[190,538,455],{"class":360},[190,540,28],{"class":213},[190,542,543],{"class":200},"encode",[190,545,546],{"class":213},"()).",[190,548,435],{"class":200},[190,550,438],{"class":213},[190,552,441],{"class":272},[190,554,555],{"class":213},")",[190,557,484],{"class":268},[190,559,560],{"class":200}," n",[190,562,463],{"class":213},[190,564,566,568,570,572,575,578,580],{"class":192,"line":565},19,[190,567,450],{"class":404},[190,569,260],{"class":213},[190,571,455],{"class":360},[190,573,574],{"class":353},"\\n",[190,576,577],{"class":364},"Scalar r = ",[190,579,455],{"class":360},[190,581,463],{"class":213},[190,583,585,587,589,592,594,596],{"class":192,"line":584},20,[190,586,450],{"class":404},[190,588,260],{"class":213},[190,590,591],{"class":404},"hex",[190,593,260],{"class":213},[190,595,180],{"class":200},[190,597,276],{"class":213},[190,599,601],{"class":192,"line":600},21,[190,602,239],{"emptyLinePlaceholder":238},[190,604,606],{"class":192,"line":605},22,[190,607,608],{"class":245},"# Get the original G point\n",[190,610,612,615,617,619,621,623,625,627,629,631,634],{"class":192,"line":611},23,[190,613,614],{"class":200},"G ",[190,616,255],{"class":213},[190,618,390],{"class":200},[190,620,28],{"class":213},[190,622,395],{"class":200},[190,624,260],{"class":213},[190,626,309],{"class":200},[190,628,260],{"class":213},[190,630,269],{"class":268},[190,632,633],{"class":272},"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",[190,635,276],{"class":213},[190,637,639],{"class":192,"line":638},24,[190,640,239],{"emptyLinePlaceholder":238},[190,642,644],{"class":192,"line":643},25,[190,645,646],{"class":245},"# Calculate rG\n",[190,648,650,653,655,658,661],{"class":192,"line":649},26,[190,651,652],{"class":200},"rG ",[190,654,255],{"class":213},[190,656,657],{"class":200}," r ",[190,659,660],{"class":268},"*",[190,662,663],{"class":200}," G\n",[190,665,667],{"class":192,"line":666},27,[190,668,239],{"emptyLinePlaceholder":238},[190,670,672],{"class":192,"line":671},28,[190,673,674],{"class":245},"# Calculate final NUMS point: H + rG\n",[190,676,678,681,683,686,689],{"class":192,"line":677},29,[190,679,680],{"class":200},"final_nums ",[190,682,255],{"class":213},[190,684,685],{"class":200}," H ",[190,687,688],{"class":268},"+",[190,690,691],{"class":200}," rG\n",[190,693,695,697,699,701,703,706,708],{"class":192,"line":694},30,[190,696,450],{"class":404},[190,698,260],{"class":213},[190,700,455],{"class":360},[190,702,574],{"class":353},[190,704,705],{"class":364},"Final NUMS point (H + rG) = ",[190,707,455],{"class":360},[190,709,463],{"class":213},[190,711,713,715,717,719,721,723,725,727,730,732,734],{"class":192,"line":712},31,[190,714,450],{"class":404},[190,716,260],{"class":213},[190,718,473],{"class":360},[190,720,476],{"class":353},[190,722,479],{"class":353},[190,724,473],{"class":360},[190,726,484],{"class":268},[190,728,729],{"class":200}," final_nums",[190,731,28],{"class":213},[190,733,492],{"class":200},[190,735,495],{"class":213},[190,737,739],{"class":192,"line":738},32,[190,740,239],{"emptyLinePlaceholder":238},[190,742,744],{"class":192,"line":743},33,[190,745,746],{"class":245},"# NUMS = 8335b42143dd67da2ec8cb8b9108777c351b47993bba2a537a04bf72eb7396a\n",[15,748,749,750,755,756,759,760,762,763,766,767,772],{},"Our first example is ",[22,751,754],{"href":752,"rel":753},"https:\u002F\u002Fgithub.com\u002Fstutxo\u002Flabitbu",[26],"Labitbu",". Rather than lifting one fixed hash to the curve, it hashes a tag together with a counter and reads the digest straight off as an x-only key: ",[51,757,758],{},"PK = SHA256(tag || ctr)",". The tag is just a label string (here, ",[51,761,754],{},", so the call is ",[51,764,765],{},"nums_from_tag(b\"Labitbu\")",") that domain-separates the key so it can't coincide with one derived for some other purpose, and the counter is bumped until the digest lands on a valid x coordinate. You can ",[22,768,771],{"href":769,"rel":770},"https:\u002F\u002Fnums-secp256k1.jaonoctus.dev\u002F?pk=96053db5b18967b5a410326ecca687441579225a6d190f398e2180deec6e429e&method=TAGGED_HASH_KEY&input=Labitbu",[26],"check the resulting point"," in the NUMS explorer.",[143,774,778],{"className":775,"code":776,"language":777,"meta":151,"style":151},"language-rust shiki shiki-themes vitesse-dark","fn nums_from_tag(tag: &[u8]) -> XOnlyPublicKey {\n    let mut ctr = 0u32;\n    loop {\n        let mut eng = sha256::Hash::engine();\n        eng.input(tag);\n        eng.input(&ctr.to_le_bytes());\n        let candidate = sha256::Hash::from_engine(eng);\n\n        if let Ok(pk) = XOnlyPublicKey::from_slice(&candidate[..]) {\n            return pk;\n        }\n        ctr += 1;\n    }\n}\n","rust",[51,779,780,820,842,849,878,895,919,946,950,993,1003,1008,1021,1026],{"__ignoreMap":151},[190,781,782,785,789,791,795,798,801,804,808,811,814,817],{"class":192,"line":193},[190,783,784],{"class":196},"fn",[190,786,788],{"class":787},"sCK9x"," nums_from_tag",[190,790,260],{"class":213},[190,792,794],{"class":793},"st-jp","tag",[190,796,797],{"class":268},":",[190,799,800],{"class":268}," &",[190,802,803],{"class":213},"[",[190,805,807],{"class":806},"syEag","u8",[190,809,810],{"class":213},"])",[190,812,813],{"class":268}," ->",[190,815,816],{"class":806}," XOnlyPublicKey",[190,818,819],{"class":213}," {\n",[190,821,822,825,828,831,833,836,839],{"class":192,"line":204},[190,823,824],{"class":268},"    let",[190,826,827],{"class":268}," mut",[190,829,830],{"class":793}," ctr",[190,832,357],{"class":213},[190,834,835],{"class":272}," 0",[190,837,838],{"class":806},"u32",[190,840,841],{"class":213},";\n",[190,843,844,847],{"class":192,"line":235},[190,845,846],{"class":196},"    loop",[190,848,819],{"class":213},[190,850,851,854,856,859,861,864,867,870,872,875],{"class":192,"line":242},[190,852,853],{"class":268},"        let",[190,855,827],{"class":268},[190,857,858],{"class":793}," eng",[190,860,357],{"class":213},[190,862,863],{"class":787}," sha256",[190,865,866],{"class":268},"::",[190,868,869],{"class":806},"Hash",[190,871,866],{"class":268},[190,873,874],{"class":787},"engine",[190,876,877],{"class":213},"();\n",[190,879,880,883,885,888,890,892],{"class":192,"line":249},[190,881,882],{"class":793},"        eng",[190,884,28],{"class":268},[190,886,887],{"class":787},"input",[190,889,260],{"class":213},[190,891,794],{"class":793},[190,893,894],{"class":213},");\n",[190,896,897,899,901,903,905,908,911,913,916],{"class":192,"line":279},[190,898,882],{"class":793},[190,900,28],{"class":268},[190,902,887],{"class":787},[190,904,260],{"class":213},[190,906,907],{"class":268},"&",[190,909,910],{"class":793},"ctr",[190,912,28],{"class":268},[190,914,915],{"class":787},"to_le_bytes",[190,917,918],{"class":213},"());\n",[190,920,921,923,926,928,930,932,934,936,939,941,944],{"class":192,"line":296},[190,922,853],{"class":268},[190,924,925],{"class":793}," candidate",[190,927,357],{"class":213},[190,929,863],{"class":787},[190,931,866],{"class":268},[190,933,869],{"class":806},[190,935,866],{"class":268},[190,937,938],{"class":787},"from_engine",[190,940,260],{"class":213},[190,942,943],{"class":793},"eng",[190,945,894],{"class":213},[190,947,948],{"class":192,"line":339},[190,949,239],{"emptyLinePlaceholder":238},[190,951,952,955,958,961,963,966,968,970,972,974,977,979,981,984,986,989,991],{"class":192,"line":344},[190,953,954],{"class":196},"        if",[190,956,957],{"class":268}," let",[190,959,960],{"class":806}," Ok",[190,962,260],{"class":213},[190,964,965],{"class":793},"pk",[190,967,555],{"class":213},[190,969,357],{"class":213},[190,971,816],{"class":806},[190,973,866],{"class":268},[190,975,976],{"class":787},"from_slice",[190,978,260],{"class":213},[190,980,907],{"class":268},[190,982,983],{"class":793},"candidate",[190,985,803],{"class":213},[190,987,988],{"class":268},"..",[190,990,810],{"class":213},[190,992,819],{"class":213},[190,994,995,998,1001],{"class":192,"line":350},[190,996,997],{"class":196},"            return",[190,999,1000],{"class":793}," pk",[190,1002,841],{"class":213},[190,1004,1005],{"class":192,"line":371},[190,1006,1007],{"class":213},"        }\n",[190,1009,1010,1013,1016,1019],{"class":192,"line":376},[190,1011,1012],{"class":793},"        ctr",[190,1014,1015],{"class":213}," +=",[190,1017,1018],{"class":272}," 1",[190,1020,841],{"class":213},[190,1022,1023],{"class":192,"line":382},[190,1024,1025],{"class":213},"    }\n",[190,1027,1028],{"class":192,"line":447},[190,1029,1030],{"class":213},"}\n",[15,1032,1033],{},[130,1034,1035,1036,28],{},"Source: ",[22,1037,1040],{"href":1038,"rel":1039},"https:\u002F\u002Fgithub.com\u002Fstutxo\u002Flabitbu\u002Fblob\u002F74350eab52a4a097dcdfb07b652d840d5767c8c5\u002Fsrc\u002Flib.rs#L394-L407",[26],"labitbu\u002Fsrc\u002Flib.rs",[15,1042,1043,1044,1049,1050,1055,1056,1059,1060,1063,1064,1066,1067,1070],{},"Another project leaning on the same trick is ",[22,1045,1048],{"href":1046,"rel":1047},"https:\u002F\u002Fwww.stratabtc.org\u002F",[26],"Strata",", from ",[22,1051,1054],{"href":1052,"rel":1053},"https:\u002F\u002Fwww.alpenlabs.io\u002F",[26],"Alpen Labs",". It takes the most minimal route of all: no counter, no tweak, not even an explicit curve lift. It hashes the label ",[51,1057,1058],{},"Strata unspendable"," with plain SHA256 and feeds the 32-byte digest straight in as an x-only public key. An x-only key ",[130,1061,1062],{},"is"," an x coordinate, so this works as long as the digest lands on the curve, and ",[51,1065,976],{}," would reject it (panicking on the ",[51,1068,1069],{},"expect",") if it hadn't:",[143,1072,1074],{"className":775,"code":1073,"language":777,"meta":151,"style":151},"pub const UNSPENDABLE_PUBLIC_KEY_INPUT: &[u8] = b\"Strata unspendable\";\npub static UNSPENDABLE_PUBLIC_KEY: LazyLock\u003CXOnlyPublicKey> = LazyLock::new(|| {\n    XOnlyPublicKey::from_slice(sha256::Hash::hash(UNSPENDABLE_PUBLIC_KEY_INPUT).as_byte_array())\n        .expect(\"valid xonly public key\")\n});\n",[51,1075,1076,1111,1151,1187,1205],{"__ignoreMap":151},[190,1077,1078,1081,1084,1087,1089,1091,1093,1095,1098,1100,1103,1105,1107,1109],{"class":192,"line":193},[190,1079,1080],{"class":196},"pub",[190,1082,1083],{"class":268}," const",[190,1085,1086],{"class":353}," UNSPENDABLE_PUBLIC_KEY_INPUT",[190,1088,797],{"class":268},[190,1090,800],{"class":268},[190,1092,803],{"class":213},[190,1094,807],{"class":806},[190,1096,1097],{"class":213},"]",[190,1099,357],{"class":213},[190,1101,1102],{"class":364}," b",[190,1104,455],{"class":360},[190,1106,1058],{"class":364},[190,1108,455],{"class":360},[190,1110,841],{"class":213},[190,1112,1113,1115,1118,1121,1123,1126,1129,1132,1135,1137,1139,1141,1144,1146,1149],{"class":192,"line":204},[190,1114,1080],{"class":196},[190,1116,1117],{"class":268}," static",[190,1119,1120],{"class":353}," UNSPENDABLE_PUBLIC_KEY",[190,1122,797],{"class":268},[190,1124,1125],{"class":806}," LazyLock",[190,1127,1128],{"class":213},"\u003C",[190,1130,1131],{"class":806},"XOnlyPublicKey",[190,1133,1134],{"class":213},">",[190,1136,357],{"class":213},[190,1138,1125],{"class":806},[190,1140,866],{"class":268},[190,1142,1143],{"class":787},"new",[190,1145,260],{"class":213},[190,1147,1148],{"class":268},"||",[190,1150,819],{"class":213},[190,1152,1153,1156,1158,1160,1162,1164,1166,1168,1170,1173,1175,1178,1180,1182,1185],{"class":192,"line":235},[190,1154,1155],{"class":787},"    XOnlyPublicKey",[190,1157,866],{"class":268},[190,1159,976],{"class":787},[190,1161,260],{"class":213},[190,1163,415],{"class":787},[190,1165,866],{"class":268},[190,1167,869],{"class":787},[190,1169,866],{"class":268},[190,1171,1172],{"class":787},"hash",[190,1174,260],{"class":213},[190,1176,1177],{"class":353},"UNSPENDABLE_PUBLIC_KEY_INPUT",[190,1179,555],{"class":213},[190,1181,28],{"class":268},[190,1183,1184],{"class":787},"as_byte_array",[190,1186,495],{"class":213},[190,1188,1189,1192,1194,1196,1198,1201,1203],{"class":192,"line":242},[190,1190,1191],{"class":268},"        .",[190,1193,1069],{"class":787},[190,1195,260],{"class":213},[190,1197,455],{"class":360},[190,1199,1200],{"class":364},"valid xonly public key",[190,1202,455],{"class":360},[190,1204,463],{"class":213},[190,1206,1207],{"class":192,"line":249},[190,1208,1209],{"class":213},"});\n",[15,1211,1212],{},[130,1213,1035,1214,28],{},[22,1215,1218],{"href":1216,"rel":1217},"https:\u002F\u002Fgithub.com\u002Falpenlabs\u002Fstrata-common\u002Fblob\u002Ff93a3b7c4f2381dca87354aa59ebadbc4bc40755\u002Fcrates\u002Fcrypto\u002Fsrc\u002Fkeys\u002Fconstants.rs#L69-L83",[26],"strata-common\u002Fcrates\u002Fcrypto\u002Fsrc\u002Fkeys\u002Fconstants.rs",[10,1220,1222],{"id":1221},"the-takeaway","The takeaway",[15,1224,1225],{},"Most public keys matter because someone holds the secret behind them. A NUMS key matters for the opposite reason: nobody does, and anyone can check that for themselves.",[15,1227,1228],{},"That second half is the whole game. The three constructions above look nothing alike (a hash lifted to the curve, a tagged hash with a counter, a bare SHA256 read straight off as an x coordinate), yet they earn the same property the same way: the recipe is public, so the result couldn't have been rigged. No trusted setup to believe in, no \"just take our word for it.\" The construction is the proof.",[15,1230,1231],{},"So when a protocol needs \"nobody should be able to do this\" to be a fact instead of a promise, a NUMS key is how you write it down.",[15,1233,1234],{},[130,1235,1236,1237],{},"PS: until a quantum computer breaks this shit. ",[51,1238,1239],{},":D",[1241,1242,1243],"style",{},"html pre.shiki code .s3QIE, html code.shiki .s3QIE{--shiki-default:#4D9375}html pre.shiki code .sNpkn, html code.shiki .sNpkn{--shiki-default:#DBD7CAEE}html pre.shiki code .s_pn2, html code.shiki .s_pn2{--shiki-default:#666666}html pre.shiki code .sux-A, html code.shiki .sux-A{--shiki-default:#758575DD}html pre.shiki code .s_wWq, html code.shiki .s_wWq{--shiki-default:#CB7676}html pre.shiki code .sxA9i, html code.shiki .sxA9i{--shiki-default:#4C9A91}html pre.shiki code .sXjYR, html code.shiki .sXjYR{--shiki-default:#C99076}html pre.shiki code .sNJcY, html code.shiki .sNJcY{--shiki-default:#C98A7D77}html pre.shiki code .s7rlk, html code.shiki .s7rlk{--shiki-default:#C98A7D}html pre.shiki code .sm68I, html code.shiki .sm68I{--shiki-default:#B8A965}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 pre.shiki code .sCK9x, html code.shiki .sCK9x{--shiki-default:#80A665}html pre.shiki code .st-jp, html code.shiki .st-jp{--shiki-default:#BD976A}html pre.shiki code .syEag, html code.shiki .syEag{--shiki-default:#5DA994}",{"title":151,"searchDepth":204,"depth":204,"links":1245},[],"2026-05-29","A NUMS point is a public key that provably has no known private key. Here's why that's useful, how it works on secp256k1, and where Bitcoin's Taproot relies on one.","md",{},"\u002F2026-05-29-secp256k1-nums-public-keys",{"title":5,"description":1247},"2026-05-29-secp256k1-nums-public-keys","NFqb9DgRComMqKiFU8ad0DOvHOrr85W8cIbK-NnrIFo",1780086317114]