[{"data":1,"prerenderedAt":1577},["ShallowReactive",2],{"posts":3},[4,1255,1330],{"id":5,"title":6,"body":7,"date":1247,"description":1248,"extension":1249,"meta":1250,"navigation":239,"path":1251,"seo":1252,"stem":1253,"__hash__":1254},"content\u002F2026-05-29-secp256k1-nums-public-keys.md","secp256k1 NUMS Public Keys",{"type":8,"value":9,"toc":1245},"minimark",[10,15,19,30,39,43,73,85,102,106,114,121,128,134,143,153,159,183,748,774,1032,1042,1072,1211,1220,1224,1227,1230,1233,1241],[11,12,14],"h1",{"id":13},"what-does-nums-even-mean","What does NUMS even mean?",[16,17,18],"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.",[16,20,21,22,29],{},"If you want a great primer on the idea, Computerphile has a video on it: ",[23,24,28],"a",{"href":25,"rel":26},"https:\u002F\u002Fyoutu.be\u002FoJWwaQm-Exs",[27],"nofollow","Magic \"Nothing Up My Sleeve\" Numbers",".",[16,31,32,33,38],{},"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 ",[23,34,37],{"href":35,"rel":36},"https:\u002F\u002Fwww.schneier.com\u002Fessays\u002Farchives\u002F2007\u002F11\u002Fdid_nsa_put_a_secret.html",[27],"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.",[11,40,42],{"id":41},"a-nums-key-on-secp256k1","A NUMS key on secp256k1",[16,44,45,50,51,55,56,59,60,63,64,67,68,29],{},[23,46,49],{"href":47,"rel":48},"https:\u002F\u002Fen.bitcoin.it\u002Fwiki\u002FSecp256k1",[27],"secp256k1"," is the elliptic curve Bitcoin uses. On that curve, a private key is just a number (technically a scalar in the range ",[52,53,54],"code",{},"1"," to ",[52,57,58],{},"n - 1",", where ",[52,61,62],{},"n"," is the order of the curve), and the matching public key is that number multiplied by a fixed point called the generator ",[52,65,66],{},"G",". The whole thing is secure because going backwards (figuring out the private key from the public key) is computationally hopeless. That's the ",[23,69,72],{"href":70,"rel":71},"https:\u002F\u002Fmath.mit.edu\u002Fclasses\u002F18.783\u002F2022\u002FLectureNotes9.pdf",[27],"discrete logarithm problem",[16,74,75,76,80,81,84],{},"A NUMS key flips the usual goal on its head. Normally you want a public key whose private key only ",[77,78,79],"strong",{},"you"," know. A NUMS key is a public key whose private key ",[77,82,83],{},"nobody"," knows, including the person who created it.",[16,86,87,88,91,92,95,96,98,99,29],{},"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 ",[52,89,90],{},"y² = x³ + 7",", that just means solving for ",[52,93,94],{},"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 ",[52,97,62],{},", the curve order, or roughly 1 in ",[52,100,101],{},"2²⁵⁶",[11,103,105],{"id":104},"so-how-is-this-useful","So how is this useful?",[16,107,108,109,29],{},"The cleanest real world example is ",[23,110,113],{"href":111,"rel":112},"https:\u002F\u002Flearnmeabitcoin.com\u002Ftechnical\u002Fupgrades\u002Ftaproot\u002F",[27],"Taproot",[16,115,116,117,120],{},"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 ",[77,118,119],{},"only"," be spent through the scripts, with the key path completely disabled.",[16,122,123],{},[124,125],"img",{"alt":126,"src":127},"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",[16,129,130],{},[131,132,133],"em",{},"Taproot Summary, from learnmeabitcoin.com.",[16,135,136,137,142],{},"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 ",[23,138,141],{"href":139,"rel":140},"https:\u002F\u002Fbips.dev\u002F341\u002F",[27],"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:",[144,145,150],"pre",{"className":146,"code":148,"language":149},[147],"language-text","H = lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)\n","text",[52,151,148],{"__ignoreMap":152},"",[16,154,155,156,158],{},"That hex value is the SHA256 of the encoded generator ",[52,157,66],{},". 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.",[16,160,161,162,167,168,171,172,174,175,178,179,182],{},"Here's the whole thing in ",[23,163,166],{"href":164,"rel":165},"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==",[27],"SageMath",": derive the base point ",[52,169,170],{},"H"," from ",[52,173,66],{},", add ",[52,176,177],{},"r * G"," for a scalar ",[52,180,181],{},"r"," that itself comes from hashing a string, and you get a NUMS point that nobody holds the key for.",[144,184,188],{"className":185,"code":186,"language":187,"meta":152,"style":152},"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",[52,189,190,203,234,241,248,278,295,338,343,349,370,375,381,446,465,497,502,508,564,583,599,604,610,637,642,648,665,670,676,693,711,737,742],{"__ignoreMap":152},[191,192,195,199],"span",{"class":193,"line":194},"line",1,[191,196,198],{"class":197},"s3QIE","import",[191,200,202],{"class":201},"sNpkn"," hashlib\n",[191,204,206,209,212,215,218,220,223,226,229,231],{"class":193,"line":205},2,[191,207,208],{"class":197},"from",[191,210,211],{"class":201}," sage",[191,213,29],{"class":214},"s_pn2",[191,216,217],{"class":201},"all ",[191,219,198],{"class":197},[191,221,222],{"class":201}," FiniteField",[191,224,225],{"class":214},",",[191,227,228],{"class":201}," EllipticCurve",[191,230,225],{"class":214},[191,232,233],{"class":201}," Integer\n",[191,235,237],{"class":193,"line":236},3,[191,238,240],{"emptyLinePlaceholder":239},true,"\n",[191,242,244],{"class":193,"line":243},4,[191,245,247],{"class":246},"sux-A","# secp256k1 parameters\n",[191,249,251,254,257,259,262,265,267,271,275],{"class":193,"line":250},5,[191,252,253],{"class":201},"F ",[191,255,256],{"class":214},"=",[191,258,222],{"class":201},[191,260,261],{"class":214},"(",[191,263,264],{"class":201},"Integer",[191,266,261],{"class":214},[191,268,270],{"class":269},"s_wWq","0x",[191,272,274],{"class":273},"sxA9i","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",[191,276,277],{"class":214},"))\n",[191,279,281,284,286,289,292],{"class":193,"line":280},6,[191,282,283],{"class":201},"n ",[191,285,256],{"class":214},[191,287,288],{"class":269}," 0x",[191,290,291],{"class":273},"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",[191,293,294],{"class":246},"  # curve order\n",[191,296,298,301,303,305,308,311,313,315,317,320,323,326,328,330,332,335],{"class":193,"line":297},7,[191,299,300],{"class":201},"E ",[191,302,256],{"class":214},[191,304,228],{"class":201},[191,306,307],{"class":214},"([",[191,309,310],{"class":201},"F",[191,312,261],{"class":214},[191,314,264],{"class":201},[191,316,261],{"class":214},[191,318,319],{"class":273},"0",[191,321,322],{"class":214},")),",[191,324,325],{"class":201}," F",[191,327,261],{"class":214},[191,329,264],{"class":201},[191,331,261],{"class":214},[191,333,334],{"class":273},"7",[191,336,337],{"class":214},"))])\n",[191,339,341],{"class":193,"line":340},8,[191,342,240],{"emptyLinePlaceholder":239},[191,344,346],{"class":193,"line":345},9,[191,347,348],{"class":246},"# Original generator point G in uncompressed format\n",[191,350,352,356,359,363,367],{"class":193,"line":351},10,[191,353,355],{"class":354},"sXjYR","G_DER",[191,357,358],{"class":214}," =",[191,360,362],{"class":361},"sNJcY"," '",[191,364,366],{"class":365},"s7rlk","0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",[191,368,369],{"class":361},"'\n",[191,371,373],{"class":193,"line":372},11,[191,374,240],{"emptyLinePlaceholder":239},[191,376,378],{"class":193,"line":377},12,[191,379,380],{"class":246},"# Get H point by hashing G\n",[191,382,384,387,389,392,394,397,399,401,403,407,409,412,414,417,419,422,424,427,429,431,434,437,440,443],{"class":193,"line":383},13,[191,385,386],{"class":201},"H ",[191,388,256],{"class":214},[191,390,391],{"class":201}," E",[191,393,29],{"class":214},[191,395,396],{"class":201},"lift_x",[191,398,261],{"class":214},[191,400,310],{"class":201},[191,402,261],{"class":214},[191,404,406],{"class":405},"sm68I","int",[191,408,261],{"class":214},[191,410,411],{"class":201},"hashlib",[191,413,29],{"class":214},[191,415,416],{"class":201},"sha256",[191,418,261],{"class":214},[191,420,421],{"class":405},"bytes",[191,423,29],{"class":214},[191,425,426],{"class":201},"fromhex",[191,428,261],{"class":214},[191,430,355],{"class":354},[191,432,433],{"class":214},")).",[191,435,436],{"class":201},"hexdigest",[191,438,439],{"class":214},"(),",[191,441,442],{"class":273}," 16",[191,444,445],{"class":214},")))\n",[191,447,449,452,454,457,460,462],{"class":193,"line":448},14,[191,450,451],{"class":405},"print",[191,453,261],{"class":214},[191,455,456],{"class":361},"\"",[191,458,459],{"class":365},"H (NUMS base point) = ",[191,461,456],{"class":361},[191,463,464],{"class":214},")\n",[191,466,468,470,472,475,478,481,483,486,489,491,494],{"class":193,"line":467},15,[191,469,451],{"class":405},[191,471,261],{"class":214},[191,473,474],{"class":361},"'",[191,476,477],{"class":354},"%x",[191,479,480],{"class":354}," %x",[191,482,474],{"class":361},[191,484,485],{"class":269}," %",[191,487,488],{"class":201}," H",[191,490,29],{"class":214},[191,492,493],{"class":201},"xy",[191,495,496],{"class":214},"())\n",[191,498,500],{"class":193,"line":499},16,[191,501,240],{"emptyLinePlaceholder":239},[191,503,505],{"class":193,"line":504},17,[191,506,507],{"class":246},"# Generate scalar from string \"unspendable\"\n",[191,509,511,514,516,519,521,523,525,527,529,531,533,535,538,540,542,545,548,550,552,554,557,559,562],{"class":193,"line":510},18,[191,512,513],{"class":201},"r ",[191,515,256],{"class":214},[191,517,518],{"class":201}," Integer",[191,520,261],{"class":214},[191,522,406],{"class":405},[191,524,261],{"class":214},[191,526,411],{"class":201},[191,528,29],{"class":214},[191,530,416],{"class":201},[191,532,261],{"class":214},[191,534,456],{"class":361},[191,536,537],{"class":365},"unspendable",[191,539,456],{"class":361},[191,541,29],{"class":214},[191,543,544],{"class":201},"encode",[191,546,547],{"class":214},"()).",[191,549,436],{"class":201},[191,551,439],{"class":214},[191,553,442],{"class":273},[191,555,556],{"class":214},")",[191,558,485],{"class":269},[191,560,561],{"class":201}," n",[191,563,464],{"class":214},[191,565,567,569,571,573,576,579,581],{"class":193,"line":566},19,[191,568,451],{"class":405},[191,570,261],{"class":214},[191,572,456],{"class":361},[191,574,575],{"class":354},"\\n",[191,577,578],{"class":365},"Scalar r = ",[191,580,456],{"class":361},[191,582,464],{"class":214},[191,584,586,588,590,593,595,597],{"class":193,"line":585},20,[191,587,451],{"class":405},[191,589,261],{"class":214},[191,591,592],{"class":405},"hex",[191,594,261],{"class":214},[191,596,181],{"class":201},[191,598,277],{"class":214},[191,600,602],{"class":193,"line":601},21,[191,603,240],{"emptyLinePlaceholder":239},[191,605,607],{"class":193,"line":606},22,[191,608,609],{"class":246},"# Get the original G point\n",[191,611,613,616,618,620,622,624,626,628,630,632,635],{"class":193,"line":612},23,[191,614,615],{"class":201},"G ",[191,617,256],{"class":214},[191,619,391],{"class":201},[191,621,29],{"class":214},[191,623,396],{"class":201},[191,625,261],{"class":214},[191,627,310],{"class":201},[191,629,261],{"class":214},[191,631,270],{"class":269},[191,633,634],{"class":273},"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",[191,636,277],{"class":214},[191,638,640],{"class":193,"line":639},24,[191,641,240],{"emptyLinePlaceholder":239},[191,643,645],{"class":193,"line":644},25,[191,646,647],{"class":246},"# Calculate rG\n",[191,649,651,654,656,659,662],{"class":193,"line":650},26,[191,652,653],{"class":201},"rG ",[191,655,256],{"class":214},[191,657,658],{"class":201}," r ",[191,660,661],{"class":269},"*",[191,663,664],{"class":201}," G\n",[191,666,668],{"class":193,"line":667},27,[191,669,240],{"emptyLinePlaceholder":239},[191,671,673],{"class":193,"line":672},28,[191,674,675],{"class":246},"# Calculate final NUMS point: H + rG\n",[191,677,679,682,684,687,690],{"class":193,"line":678},29,[191,680,681],{"class":201},"final_nums ",[191,683,256],{"class":214},[191,685,686],{"class":201}," H ",[191,688,689],{"class":269},"+",[191,691,692],{"class":201}," rG\n",[191,694,696,698,700,702,704,707,709],{"class":193,"line":695},30,[191,697,451],{"class":405},[191,699,261],{"class":214},[191,701,456],{"class":361},[191,703,575],{"class":354},[191,705,706],{"class":365},"Final NUMS point (H + rG) = ",[191,708,456],{"class":361},[191,710,464],{"class":214},[191,712,714,716,718,720,722,724,726,728,731,733,735],{"class":193,"line":713},31,[191,715,451],{"class":405},[191,717,261],{"class":214},[191,719,474],{"class":361},[191,721,477],{"class":354},[191,723,480],{"class":354},[191,725,474],{"class":361},[191,727,485],{"class":269},[191,729,730],{"class":201}," final_nums",[191,732,29],{"class":214},[191,734,493],{"class":201},[191,736,496],{"class":214},[191,738,740],{"class":193,"line":739},32,[191,741,240],{"emptyLinePlaceholder":239},[191,743,745],{"class":193,"line":744},33,[191,746,747],{"class":246},"# NUMS = 8335b42143dd67da2ec8cb8b9108777c351b47993bba2a537a04bf72eb7396a\n",[16,749,750,751,756,757,760,761,763,764,767,768,773],{},"Our first example is ",[23,752,755],{"href":753,"rel":754},"https:\u002F\u002Fgithub.com\u002Fstutxo\u002Flabitbu",[27],"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: ",[52,758,759],{},"PK = SHA256(tag || ctr)",". The tag is just a label string (here, ",[52,762,755],{},", so the call is ",[52,765,766],{},"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 ",[23,769,772],{"href":770,"rel":771},"https:\u002F\u002Fnums-secp256k1.jaonoctus.dev\u002F?pk=96053db5b18967b5a410326ecca687441579225a6d190f398e2180deec6e429e&method=TAGGED_HASH_KEY&input=Labitbu",[27],"check the resulting point"," in the NUMS explorer.",[144,775,779],{"className":776,"code":777,"language":778,"meta":152,"style":152},"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",[52,780,781,821,843,850,879,896,920,947,951,994,1004,1009,1022,1027],{"__ignoreMap":152},[191,782,783,786,790,792,796,799,802,805,809,812,815,818],{"class":193,"line":194},[191,784,785],{"class":197},"fn",[191,787,789],{"class":788},"sCK9x"," nums_from_tag",[191,791,261],{"class":214},[191,793,795],{"class":794},"st-jp","tag",[191,797,798],{"class":269},":",[191,800,801],{"class":269}," &",[191,803,804],{"class":214},"[",[191,806,808],{"class":807},"syEag","u8",[191,810,811],{"class":214},"])",[191,813,814],{"class":269}," ->",[191,816,817],{"class":807}," XOnlyPublicKey",[191,819,820],{"class":214}," {\n",[191,822,823,826,829,832,834,837,840],{"class":193,"line":205},[191,824,825],{"class":269},"    let",[191,827,828],{"class":269}," mut",[191,830,831],{"class":794}," ctr",[191,833,358],{"class":214},[191,835,836],{"class":273}," 0",[191,838,839],{"class":807},"u32",[191,841,842],{"class":214},";\n",[191,844,845,848],{"class":193,"line":236},[191,846,847],{"class":197},"    loop",[191,849,820],{"class":214},[191,851,852,855,857,860,862,865,868,871,873,876],{"class":193,"line":243},[191,853,854],{"class":269},"        let",[191,856,828],{"class":269},[191,858,859],{"class":794}," eng",[191,861,358],{"class":214},[191,863,864],{"class":788}," sha256",[191,866,867],{"class":269},"::",[191,869,870],{"class":807},"Hash",[191,872,867],{"class":269},[191,874,875],{"class":788},"engine",[191,877,878],{"class":214},"();\n",[191,880,881,884,886,889,891,893],{"class":193,"line":250},[191,882,883],{"class":794},"        eng",[191,885,29],{"class":269},[191,887,888],{"class":788},"input",[191,890,261],{"class":214},[191,892,795],{"class":794},[191,894,895],{"class":214},");\n",[191,897,898,900,902,904,906,909,912,914,917],{"class":193,"line":280},[191,899,883],{"class":794},[191,901,29],{"class":269},[191,903,888],{"class":788},[191,905,261],{"class":214},[191,907,908],{"class":269},"&",[191,910,911],{"class":794},"ctr",[191,913,29],{"class":269},[191,915,916],{"class":788},"to_le_bytes",[191,918,919],{"class":214},"());\n",[191,921,922,924,927,929,931,933,935,937,940,942,945],{"class":193,"line":297},[191,923,854],{"class":269},[191,925,926],{"class":794}," candidate",[191,928,358],{"class":214},[191,930,864],{"class":788},[191,932,867],{"class":269},[191,934,870],{"class":807},[191,936,867],{"class":269},[191,938,939],{"class":788},"from_engine",[191,941,261],{"class":214},[191,943,944],{"class":794},"eng",[191,946,895],{"class":214},[191,948,949],{"class":193,"line":340},[191,950,240],{"emptyLinePlaceholder":239},[191,952,953,956,959,962,964,967,969,971,973,975,978,980,982,985,987,990,992],{"class":193,"line":345},[191,954,955],{"class":197},"        if",[191,957,958],{"class":269}," let",[191,960,961],{"class":807}," Ok",[191,963,261],{"class":214},[191,965,966],{"class":794},"pk",[191,968,556],{"class":214},[191,970,358],{"class":214},[191,972,817],{"class":807},[191,974,867],{"class":269},[191,976,977],{"class":788},"from_slice",[191,979,261],{"class":214},[191,981,908],{"class":269},[191,983,984],{"class":794},"candidate",[191,986,804],{"class":214},[191,988,989],{"class":269},"..",[191,991,811],{"class":214},[191,993,820],{"class":214},[191,995,996,999,1002],{"class":193,"line":351},[191,997,998],{"class":197},"            return",[191,1000,1001],{"class":794}," pk",[191,1003,842],{"class":214},[191,1005,1006],{"class":193,"line":372},[191,1007,1008],{"class":214},"        }\n",[191,1010,1011,1014,1017,1020],{"class":193,"line":377},[191,1012,1013],{"class":794},"        ctr",[191,1015,1016],{"class":214}," +=",[191,1018,1019],{"class":273}," 1",[191,1021,842],{"class":214},[191,1023,1024],{"class":193,"line":383},[191,1025,1026],{"class":214},"    }\n",[191,1028,1029],{"class":193,"line":448},[191,1030,1031],{"class":214},"}\n",[16,1033,1034],{},[131,1035,1036,1037,29],{},"Source: ",[23,1038,1041],{"href":1039,"rel":1040},"https:\u002F\u002Fgithub.com\u002Fstutxo\u002Flabitbu\u002Fblob\u002F74350eab52a4a097dcdfb07b652d840d5767c8c5\u002Fsrc\u002Flib.rs#L394-L407",[27],"labitbu\u002Fsrc\u002Flib.rs",[16,1043,1044,1045,1050,1051,1056,1057,1060,1061,1064,1065,1067,1068,1071],{},"Another project leaning on the same trick is ",[23,1046,1049],{"href":1047,"rel":1048},"https:\u002F\u002Fwww.stratabtc.org\u002F",[27],"Strata",", from ",[23,1052,1055],{"href":1053,"rel":1054},"https:\u002F\u002Fwww.alpenlabs.io\u002F",[27],"Alpen Labs",". It takes the most minimal route of all: no counter, no tweak, not even an explicit curve lift. It hashes the label ",[52,1058,1059],{},"Strata unspendable"," with plain SHA256 and feeds the 32-byte digest straight in as an x-only public key. An x-only key ",[131,1062,1063],{},"is"," an x coordinate, so this works as long as the digest lands on the curve, and ",[52,1066,977],{}," would reject it (panicking on the ",[52,1069,1070],{},"expect",") if it hadn't:",[144,1073,1075],{"className":776,"code":1074,"language":778,"meta":152,"style":152},"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",[52,1076,1077,1112,1152,1188,1206],{"__ignoreMap":152},[191,1078,1079,1082,1085,1088,1090,1092,1094,1096,1099,1101,1104,1106,1108,1110],{"class":193,"line":194},[191,1080,1081],{"class":197},"pub",[191,1083,1084],{"class":269}," const",[191,1086,1087],{"class":354}," UNSPENDABLE_PUBLIC_KEY_INPUT",[191,1089,798],{"class":269},[191,1091,801],{"class":269},[191,1093,804],{"class":214},[191,1095,808],{"class":807},[191,1097,1098],{"class":214},"]",[191,1100,358],{"class":214},[191,1102,1103],{"class":365}," b",[191,1105,456],{"class":361},[191,1107,1059],{"class":365},[191,1109,456],{"class":361},[191,1111,842],{"class":214},[191,1113,1114,1116,1119,1122,1124,1127,1130,1133,1136,1138,1140,1142,1145,1147,1150],{"class":193,"line":205},[191,1115,1081],{"class":197},[191,1117,1118],{"class":269}," static",[191,1120,1121],{"class":354}," UNSPENDABLE_PUBLIC_KEY",[191,1123,798],{"class":269},[191,1125,1126],{"class":807}," LazyLock",[191,1128,1129],{"class":214},"\u003C",[191,1131,1132],{"class":807},"XOnlyPublicKey",[191,1134,1135],{"class":214},">",[191,1137,358],{"class":214},[191,1139,1126],{"class":807},[191,1141,867],{"class":269},[191,1143,1144],{"class":788},"new",[191,1146,261],{"class":214},[191,1148,1149],{"class":269},"||",[191,1151,820],{"class":214},[191,1153,1154,1157,1159,1161,1163,1165,1167,1169,1171,1174,1176,1179,1181,1183,1186],{"class":193,"line":236},[191,1155,1156],{"class":788},"    XOnlyPublicKey",[191,1158,867],{"class":269},[191,1160,977],{"class":788},[191,1162,261],{"class":214},[191,1164,416],{"class":788},[191,1166,867],{"class":269},[191,1168,870],{"class":788},[191,1170,867],{"class":269},[191,1172,1173],{"class":788},"hash",[191,1175,261],{"class":214},[191,1177,1178],{"class":354},"UNSPENDABLE_PUBLIC_KEY_INPUT",[191,1180,556],{"class":214},[191,1182,29],{"class":269},[191,1184,1185],{"class":788},"as_byte_array",[191,1187,496],{"class":214},[191,1189,1190,1193,1195,1197,1199,1202,1204],{"class":193,"line":243},[191,1191,1192],{"class":269},"        .",[191,1194,1070],{"class":788},[191,1196,261],{"class":214},[191,1198,456],{"class":361},[191,1200,1201],{"class":365},"valid xonly public key",[191,1203,456],{"class":361},[191,1205,464],{"class":214},[191,1207,1208],{"class":193,"line":250},[191,1209,1210],{"class":214},"});\n",[16,1212,1213],{},[131,1214,1036,1215,29],{},[23,1216,1219],{"href":1217,"rel":1218},"https:\u002F\u002Fgithub.com\u002Falpenlabs\u002Fstrata-common\u002Fblob\u002Ff93a3b7c4f2381dca87354aa59ebadbc4bc40755\u002Fcrates\u002Fcrypto\u002Fsrc\u002Fkeys\u002Fconstants.rs#L69-L83",[27],"strata-common\u002Fcrates\u002Fcrypto\u002Fsrc\u002Fkeys\u002Fconstants.rs",[11,1221,1223],{"id":1222},"the-takeaway","The takeaway",[16,1225,1226],{},"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.",[16,1228,1229],{},"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.",[16,1231,1232],{},"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.",[16,1234,1235],{},[131,1236,1237,1238],{},"PS: until a quantum computer breaks this shit. ",[52,1239,1240],{},":D",[1242,1243,1244],"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":152,"searchDepth":205,"depth":205,"links":1246},[],"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":6,"description":1248},"2026-05-29-secp256k1-nums-public-keys","NFqb9DgRComMqKiFU8ad0DOvHOrr85W8cIbK-NnrIFo",{"id":1256,"title":1257,"body":1258,"date":1323,"description":1324,"extension":1249,"meta":1325,"navigation":239,"path":1326,"seo":1327,"stem":1328,"__hash__":1329},"content\u002F2017-10-02-facebook-messenger-botathon-cpbr10.md","2nd Place at the Facebook Messenger Hackathon at Campus Party",{"type":8,"value":1259,"toc":1321},[1260,1266,1271,1274,1280,1287,1290,1295,1298,1304,1309,1312,1315],[16,1261,1262],{},[124,1263],{"alt":1264,"src":1265},"The team and other participants on stage at the Facebook Messenger Botathon, in front of a Submarino and BLiP backdrop.","\u002Fimages\u002Fcpbr10-botathon-team.webp",[16,1267,1268],{},[131,1269,1270],{},"The team was awarded at the 10th edition of Campus Party Brazil (#CPBR10).",[16,1272,1273],{},"My name is João Dias and I was 18 at the time of the event. It took me exactly\n39 hours and 59 minutes to complete a challenge launched by Facebook, the\nFacebook Messenger Botathon, at the 10th edition of Campus Party (#CPBR10),\nheld in January and February 2017.",[1275,1276,1277],"blockquote",{},[16,1278,1279],{},"Yes, we hit the submit button in the last minute.",[16,1281,1282,1283,1286],{},"Diego Rangel and Douglas Lacerda (two people I met at the conference who later\nbecame friends) and I competed against 95 teams from across the country. We won\nsecond place and took home the FbStart\nAccelerate track code, worth US$80,000 in investment to keep developing our\nproject: ",[77,1284,1285],{},"ezBOT",", a bot that creates automated messages for anyone running a\nbusiness page on Facebook.",[16,1288,1289],{},"Facebook had announced ahead of time that it would launch a challenge at the\nevent, though it hadn't said exactly what. It was during Campus Party that the\nworld's largest social network told the public what the challenge was: build a\nMessenger bot that solves a real-world problem and uses at least 6 of the\nplatform's features. Teams had up to 40 hours to pull it off.",[1275,1291,1292],{},[16,1293,1294],{},"We spent nearly 40 hours at the workbenches running on coffee and energy\ndrinks. The adrenaline hit in the final minutes; my heart clenched at the\nthought of not finishing in time.",[16,1296,1297],{},"The project that took second place was built around a real need of Facebook\npage admins who run a business. The shortest way I can put it: \"it's a bot that\nbuilds bots.\" But to make it clearer what it actually is, take a look at how it\nworks:",[16,1299,1300],{},[124,1301],{"alt":1302,"src":1303},"ezBOT Messenger conversation showing the automated-response template, with a \"Questions & Answers\" card the page owner can pick.","\u002Fimages\u002Fezbot-messenger.webp",[16,1305,1306],{},[131,1307,1308],{},"Screen showing the automated-response model, with questions and answers.",[16,1310,1311],{},"ezBOT lets a Facebook page set up custom interaction patterns with users who\nreach out via Messenger, like a virtual assistant. With ezBOT you can, for\nexample, create a set of questions and answers that come up often among the\ncustomers of a given product or service. So when a user contacts the page\nthrough Messenger, they get an automated reply based on what they asked. For the\nuser, that's a win in response speed; for the business owner too, since it makes\na good impression and clears up common questions very quickly. We built two more\nvirtual-assistant templates during the challenge as well.",[1313,1314],"hr",{},[16,1316,1317,1320],{},[77,1318,1319],{},"Update:"," the project has since been discontinued.",{"title":152,"searchDepth":205,"depth":205,"links":1322},[],"2017-10-02","How two people I met at the conference and I built a \"bot that builds bots\" in under 40 hours and took second place (and US$80k) at Facebook's Messenger Botathon.",{},"\u002F2017-10-02-facebook-messenger-botathon-cpbr10",{"title":1257,"description":1324},"2017-10-02-facebook-messenger-botathon-cpbr10","O9EY4S33NlbUpidDKdJmPv8tpFMt-m48SRccpn_U-CQ",{"id":1331,"title":1332,"body":1333,"date":1570,"description":1571,"extension":1249,"meta":1572,"navigation":239,"path":1573,"seo":1574,"stem":1575,"__hash__":1576},"content\u002F2017-08-16-best-bitcoin-wallet-cold-vs-hot-vs-exchange.md","What's the best wallet to store my Bitcoin? Cold Wallet vs Hot Wallet vs Exchange",{"type":8,"value":1334,"toc":1560},[1335,1341,1346,1350,1355,1358,1362,1365,1372,1375,1379,1383,1396,1400,1410,1413,1417,1421,1449,1453,1476,1480,1503,1507,1546,1548,1554],[16,1336,1337],{},[124,1338],{"alt":1339,"src":1340},"Bitcoin wallet icon, a leather wallet holding a Bitcoin coin.","\u002Fimages\u002Fbitcoin-wallet.webp",[16,1342,1343],{},[131,1344,1345],{},"Bitcoin wallet, from flaticon.com.",[11,1347,1349],{"id":1348},"first-things-first-you-need-to-know-that","First things first, you need to know that...",[1351,1352,1354],"h2",{"id":1353},"you-are-your-own-bank","You are your own bank",[16,1356,1357],{},"Bitcoin's technology brought us a new paradigm where you take on the job of\ncustodying (holding and protecting) your own money, in whatever way feels safest\nand most comfortable to you. So, unless you don't care about your money at all,\nany security measure is worth it.",[1351,1359,1361],{"id":1360},"but-what-are-you-actually-custodying","But what are you actually custodying?",[16,1363,1364],{},"You've probably got a leather wallet with a few hundred-dollar bills inside,\nright? Okay, some tens? Come on, not even a single crumpled one-dollar bill? The\npoint is, you can physically put some cash inside it. That does not happen with\nBitcoin.",[16,1366,1367,1368,1371],{},"In other words, ",[77,1369,1370],{},"bitcoins are not \"inside\" the wallet or on the websites",".\nThese are just clients (software) that let you use a pair of public and private\nkeys.",[16,1373,1374],{},"Sticking with the bank analogy, the public key would be your \"account number\"\n(on the blockchain). I just need to know it to send you something. The private\nkey would be the \"password\". I just need to know it to drain the money.",[11,1376,1378],{"id":1377},"okay-got-it-how-do-i-get-access-to-my-bitcoins","Okay, got it... How do I get access to my bitcoins?",[1351,1380,1382],{"id":1381},"exchanges-and-wallet-websites","Exchanges and wallet websites",[16,1384,1385,1386,1389,1390,1395],{},"The elders of this digital-money world are tired of repeating it... ",[77,1387,1388],{},"AN\nEXCHANGE IS NOT A WALLET",". And unless I'm going crazy, we were making an analogy\nwith a bank, right? So, when you leave your money at the bank, it isn't yours\nanymore, it's the bank's. If it goes bust or gets robbed, you can say goodbye to\nthat hard-earned money and cry in the fetal position. With bitcoins, \"it's the\nsame thing\". By leaving your coins in exchange addresses or on websites where\nsomeone else has access to your private key, you're handing your ownership over\nto a third party. And we already know the risks of that (see\n",[23,1391,1394],{"href":1392,"rel":1393},"https:\u002F\u002Ftecnoblog.net\u002Fnoticias\u002Fmtgox-fecha-bitcoins-somem\u002F",[27],"mtgox",").",[1351,1397,1399],{"id":1398},"hot-wallet-vs-cold-wallet","Hot Wallet vs Cold Wallet",[1275,1401,1402],{},[16,1403,1404,1405],{},"hot wallets are connected to the internet while cold wallets are not. - ",[23,1406,1409],{"href":1407,"rel":1408},"https:\u002F\u002Fstellabelle.medium.com\u002Fcold-wallet-vs-hot-wallet-whats-the-difference-a00d872aa6b1",[27],"Leah Stella Stephens",[16,1411,1412],{},"In short, Hot Wallets are meant to be your \"everyday\" wallet, while Cold Wallets\nare meant to be the \"vaults\" for your coins.",[11,1414,1416],{"id":1415},"wallets-i-recommend","Wallets I recommend",[1351,1418,1420],{"id":1419},"software-on-chain","Software (on-chain):",[1422,1423,1424,1432,1435,1442],"ul",{},[1425,1426,1427,1428,556],"li",{},"Sparrow (",[23,1429,1430],{"href":1430,"rel":1431},"https:\u002F\u002Fsparrowwallet.com",[27],[1425,1433,1434],{},"Samourai (RIP)",[1425,1436,1437,1438,556],{},"BlueWallet (",[23,1439,1440],{"href":1440,"rel":1441},"https:\u002F\u002Fbluewallet.io",[27],[1425,1443,1444,1445,556],{},"Electrum (",[23,1446,1447],{"href":1447,"rel":1448},"https:\u002F\u002Felectrum.org",[27],[1351,1450,1452],{"id":1451},"hardware-on-chain-for-signing-transactions-offline","Hardware (on-chain, for signing transactions offline):",[1422,1454,1455,1462,1469],{},[1425,1456,1457,1458,556],{},"Cobo Vault (",[23,1459,1460],{"href":1460,"rel":1461},"https:\u002F\u002Fcobo.com\u002Fhardware-wallet",[27],[1425,1463,1464,1465,556],{},"TrezorT (",[23,1466,1467],{"href":1467,"rel":1468},"https:\u002F\u002Ftrezor.io",[27],[1425,1470,1471,1472,556],{},"A USB stick with Tails (",[23,1473,1474],{"href":1474,"rel":1475},"https:\u002F\u002Fyoutu.be\u002FabXkgXQ8BvI",[27],[1351,1477,1479],{"id":1478},"metal-wallets-for-storing-your-seed","Metal wallets (for storing your seed):",[1422,1481,1482,1489,1496],{},[1425,1483,1484,1485,556],{},"StackBit (",[23,1486,1487],{"href":1487,"rel":1488},"https:\u002F\u002Fstackbit.me",[27],[1425,1490,1491,1492,556],{},"CryptoSteel Capsule (",[23,1493,1494],{"href":1494,"rel":1495},"https:\u002F\u002Fcryptosteel.com\u002Fproduct\u002Fcryptosteel-capsule\u002F",[27],[1425,1497,1498,1499,556],{},"Or you can make your own (",[23,1500,1501],{"href":1501,"rel":1502},"https:\u002F\u002Fyoutu.be\u002FTtgsE4Cj5D4",[27],[1351,1504,1506],{"id":1505},"software-off-chain-lightning","Software (off-chain, lightning):",[1422,1508,1509,1522,1535],{},[1425,1510,1511,1512,556,1516],{},"Phoenix (",[23,1513,1514],{"href":1514,"rel":1515},"https:\u002F\u002Fphoenix.acinq.co",[27],[1517,1518,1519],"sup",{},[23,1520,54],{"href":1521},"#fn-1",[1425,1523,1524,1525,556,1529],{},"ZBD (",[23,1526,1527],{"href":1527,"rel":1528},"https:\u002F\u002Fzebedee.io\u002F",[27],[1517,1530,1531],{},[23,1532,1534],{"href":1533},"#fn-2","2",[1425,1536,1537,1538,556,1542],{},"WOS (",[23,1539,1540],{"href":1540,"rel":1541},"https:\u002F\u002Fwalletofsatoshi.com\u002F",[27],[1517,1543,1544],{},[23,1545,1534],{"href":1533},[1313,1547],{},[16,1549,1550,1553],{},[1517,1551,54],{"id":1552},"fn-1"," sovereign (you hold the keys)",[16,1555,1556,1559],{},[1517,1557,1534],{"id":1558},"fn-2"," custodial (you don't hold the keys, they're under a third party's custody)",{"title":152,"searchDepth":205,"depth":205,"links":1561},[1562,1563,1564,1565,1566,1567,1568,1569],{"id":1353,"depth":205,"text":1354},{"id":1360,"depth":205,"text":1361},{"id":1381,"depth":205,"text":1382},{"id":1398,"depth":205,"text":1399},{"id":1419,"depth":205,"text":1420},{"id":1451,"depth":205,"text":1452},{"id":1478,"depth":205,"text":1479},{"id":1505,"depth":205,"text":1506},"2017-08-16","Bitcoin makes you your own bank. Here's the difference between cold wallets, hot wallets, and exchanges, plus the wallets I actually use to custody my coins.",{},"\u002F2017-08-16-best-bitcoin-wallet-cold-vs-hot-vs-exchange",{"title":1332,"description":1571},"2017-08-16-best-bitcoin-wallet-cold-vs-hot-vs-exchange","0--2SZPEc8ROJnM_80vbimuw6i1Vg2lBWp8OORgSa30",1780086317003]