wallet.hoon 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285
  1. :: /ker/wallet/wallet: nockchain wallet
  2. /= bip39 /common/bip39
  3. /= slip10 /common/slip10
  4. /= m /common/markdown/types
  5. /= md /common/markdown/markdown
  6. /= transact /common/tx-engine
  7. /= z /common/zeke
  8. /= zo /common/zoon
  9. /= dumb /apps/dumbnet/lib/types
  10. /= * /common/zose
  11. /= * /common/wrapper
  12. ::
  13. =>
  14. =| bug=_&
  15. |%
  16. :: $key: public or private key
  17. ::
  18. :: a public key is generated from a private key and +cc (chain code).
  19. :: pub contains the base58 encoded version of the cheetah curve point
  20. ::
  21. +$ key
  22. $~ [%pub p=*@ux]
  23. $% [%pub p=@ux]
  24. [%prv p=@ux]
  25. ==
  26. :: $coil: key and chaincode
  27. ::
  28. :: a wallet consists of a collection of +coil (address and entropy pair). the
  29. :: $cc (called a chain code elsewhere) allows for the deterministic
  30. :: generation of child keys from a parent key without compromising other
  31. :: branches of the hierarchy.
  32. ::
  33. :: .key: public or private key
  34. :: .cc: associated entropy (chain code)
  35. ::
  36. +$ coil [%coil =key =cc]
  37. ::
  38. :: $meta: stored metadata for a key
  39. +$ meta
  40. $% coil
  41. [%label @t]
  42. [%seed @t]
  43. ==
  44. ::
  45. :: $keys: path indexed map for keys
  46. ::
  47. :: path format for keys state:
  48. ::
  49. :: /keys :: root path (holds nothing in its fil)
  50. :: /keys/[t/master]/[key-type]/m/[coil/key] :: master key path
  51. :: /keys/[t/master]/[key-type]/[ud/index]/[coil/key] :: derived key path
  52. :: /keys/[t/master]/[key-type]/[ud/index]/[coil/key] :: specific key path
  53. :: /keys/[t/master]/[key-type]/[ud/index]/label/[label/label] :: key label path for derived key
  54. :: /keys/[t/master]/[key-type]/m/label/[label/label] :: key label path for master key
  55. :: /keys/[t/master]/seed/[seed/seed-phrase] :: seed-phrase path
  56. ::
  57. :: Note the terminal entry of the path holds that value, this value is the
  58. :: non-unit `fil` in the $axal definition
  59. ::
  60. :: where:
  61. :: - [t/master] is the base58 encoded master public key as @t
  62. :: - m denotes the master key
  63. :: - [ud/index] is the derivation index as @ud
  64. :: - [key-type] is either %pub or %prv
  65. :: - [coil/key] is the key and chaincode pair. key is in serialized
  66. :: format as a @ux, NOT base58.
  67. :: - [seed/seed-phrase] is the seed phrase as a tape
  68. :: - [label/label] is a label value
  69. ::
  70. :: master key is stored under 'm'.
  71. :: derived keys use incrementing indices starting from 0 under their master-key and key-type
  72. :: labels are stored as children of their associated keys.
  73. :: seed is a seed phrase and is only stored as a child of [t/master]
  74. ::
  75. +$ keys $+(keys-axal (axal meta))
  76. ::
  77. :: $draft-tree: structured tree of draft, input, and seed data
  78. ::
  79. :: we use the axal structure to track the relationship between drafts,
  80. :: inputs, and seeds. this allows us to navigate the tree and maintain
  81. :: all the relationships without duplicating data.
  82. ::
  83. :: paths in the draft-tree follow these conventions:
  84. ::
  85. :: /draft/[draft-name] :: draft node
  86. :: /draft/[draft-name]/input/[input-name] :: input in a draft
  87. :: /input/[input-name] :: input node
  88. :: /input/[input-name]/seed/[seed-name] :: seed in an input
  89. :: /seed/[seed-name] :: seed node
  90. ::
  91. +$ draft-tree
  92. $+ wallet-draft-tree
  93. (axal draft-entity)
  94. :: $draft-entity: entities stored in the draft tree
  95. ::
  96. +$ draft-entity
  97. $% [%draft =draft-name =draft]
  98. [%input =input-name =preinput]
  99. [%seed =seed-name =preseed]
  100. ==
  101. ::
  102. :: +master: master key pair
  103. ++ master
  104. =< form
  105. |%
  106. +$ form (unit coil)
  107. ++ public
  108. |= =form
  109. ~| "master public key not found"
  110. ?< ?=(~ form)
  111. u.form
  112. ::
  113. ++ to-b58
  114. |= =form
  115. ^- @t
  116. (crip (en:base58:wrap p.key:(public form)))
  117. --
  118. :: $cc: chaincode
  119. ::
  120. +$ cc @ux
  121. :: $balance: wallet balance
  122. +$ balance
  123. $+ wallet-balance
  124. (z-map:zo nname:transact nnote:transact)
  125. :: $state: wallet state
  126. ::
  127. +$ state
  128. $: %0
  129. =balance
  130. hash-to-name=(z-map:zo hash:transact nname:transact) :: hash of note -> name of note
  131. name-to-hash=(z-map:zo nname:transact hash:transact) :: name of note -> hash of note
  132. receive-address=lock:transact
  133. =master
  134. =keys
  135. transactions=$+(transactions (map * transaction))
  136. last-block=(unit block-id:transact)
  137. peek-requests=$+(peek-requests (map @ud ?(%balance %block)))
  138. active-draft=(unit draft-name)
  139. active-input=(unit input-name)
  140. active-seed=(unit seed-name) :: currently selected seed
  141. draft-tree=draft-tree :: structured tree of drafts, inputs, and seeds
  142. pending-commands=(z-map:zo @ud [phase=?(%block %balance %ready) wrapped=cause]) :: commands waiting for sync
  143. ==
  144. +$ seed-name $~('default-seed' @t)
  145. ::
  146. +$ draft-name $~('default-draft' @t)
  147. ::
  148. +$ input-name $~('default-input' @t)
  149. ::
  150. :: $transaction: TODO
  151. ::
  152. +$ transaction
  153. $: recipient=@ux
  154. amount=@ud
  155. status=?(%unsigned %signed %sent)
  156. ==
  157. ::
  158. +$ cause
  159. $% [%keygen entropy=byts salt=byts]
  160. [%derive-child key-type=?(%pub %prv) i=@ label=(unit @t)]
  161. [%import-keys keys=(list (pair trek coil))]
  162. [%import-master-pubkey key=@t cc=@t] :: base58-encoded pubkey + chain code
  163. [%make-tx dat=draft]
  164. [%list-notes-by-pubkey pubkey=@t] :: base58-encoded pubkey
  165. $: %simple-spend
  166. names=(list [first=@t last=@t]) :: base58-encoded name hashes
  167. recipients=(list [m=@ pks=(list @t)]) :: base58-encoded locks
  168. gifts=(list coins:transact) :: number of coins to spend
  169. fee=coins:transact :: fee
  170. ==
  171. [%sign-tx dat=draft index=(unit @ud) entropy=@]
  172. [%list-pubkeys ~]
  173. [%list-notes ~]
  174. [%show-balance block=@ux]
  175. [%show =path]
  176. [%gen-master-privkey seedphrase=@t]
  177. [%gen-master-pubkey master-privkey=keyc:slip10]
  178. [%update-balance ~]
  179. [%update-block ~]
  180. [%sync-run wrapped=cause] :: run command after sync completes
  181. $: %scan
  182. master-pubkey=@t :: base58 encoded master public key to scan for
  183. search-depth=$~(100 @ud) :: how many addresses to scan (default 100)
  184. include-timelocks=$~(%.n ?) :: include timelocked notes (default false)
  185. include-multisig=$~(%.n ?) :: include notes with multisigs (default false)
  186. ==
  187. [%advanced-spend advanced-spend]
  188. [%file %write path=@t contents=@t success=?]
  189. npc-cause
  190. ==
  191. ::
  192. +$ advanced-spend
  193. $% [%seed advanced-spend-seed]
  194. [%input advanced-spend-input]
  195. [%draft advanced-spend-draft]
  196. ==
  197. ::
  198. +$ advanced-spend-seed
  199. $% [%new name=@t] :: new empty seed in draft
  200. $: %set-name
  201. seed-name=@t
  202. new-name=@t
  203. ==
  204. $: %set-source :: set .output-source
  205. seed-name=@t
  206. source=(unit [hash=@t is-coinbase=?])
  207. ==
  208. $: %set-recipient :: set .recipient
  209. seed-name=@t
  210. recipient=[m=@ pks=(list @t)]
  211. ==
  212. $: %set-timelock :: set .timelock-intent
  213. seed-name=@t
  214. absolute=timelock-range:transact
  215. relative=timelock-range:transact
  216. ==
  217. $: %set-gift
  218. seed-name=@t
  219. gift=coins:transact
  220. ==
  221. $: %set-parent-hash
  222. seed-name=@t
  223. parent-hash=@t
  224. ==
  225. $: %set-parent-hash-from-name
  226. seed-name=@t
  227. name=[@t @t]
  228. ==
  229. $: %print-status :: do the needful
  230. seed-name=@t
  231. ==
  232. ==
  233. :: $seed-mask: tracks which fields of a $seed:transact have been set
  234. ::
  235. :: this might have been better as a "unitized seed" but would have been
  236. :: much more annoying to read the code
  237. +$ seed-mask
  238. $~ [%.n %.n %.n %.n %.n]
  239. $: output-source=?
  240. recipient=?
  241. timelock-intent=?
  242. gift=?
  243. parent-hash=?
  244. ==
  245. :: $preseed: a $seed:transact in process of being built
  246. +$ preseed [name=@t (pair seed:transact seed-mask)]
  247. ::
  248. :: $spend-mask: tracks which field of a $spend:transact have been set
  249. +$ spend-mask
  250. $~ [%.n %.n %.n]
  251. $: signature=?
  252. seeds=?
  253. fee=?
  254. ==
  255. ::
  256. +$ advanced-spend-input
  257. :: there is only one right way to create an $input from a $spend, so we don't need
  258. :: the mask or other commands.
  259. $% [%new name=@t] :: new empty input
  260. $: %set-name
  261. input-name=@t
  262. new-name=@t
  263. ==
  264. $: %add-seed
  265. input-name=@t
  266. seed-name=@t
  267. ==
  268. $: %set-fee
  269. input-name=@t
  270. fee=coins:transact
  271. ==
  272. $: %set-note-from-name :: set .note using .name
  273. input-name=@t
  274. name=[@t @t]
  275. ==
  276. $: %set-note-from-hash :: set .note using hash
  277. input-name=@t
  278. hash=@t
  279. ==
  280. $: %derive-note-from-seeds :: derive note from seeds
  281. input-name=@t
  282. ==
  283. $: %remove-seed
  284. input-name=@t
  285. seed-name=@t
  286. ==
  287. $: %remove-seed-by-hash
  288. input-name=@t
  289. hash=@t
  290. ==
  291. $: %print-status
  292. input-name=@t
  293. ==
  294. ==
  295. ::
  296. +$ input-mask
  297. $~ [%.n *spend-mask]
  298. $: note=?
  299. spend=spend-mask
  300. ==
  301. ::
  302. +$ preinput [name=@t (pair input:transact input-mask)]
  303. ::
  304. +$ draft [name=@t p=inputs:transact]
  305. ::
  306. +$ advanced-spend-draft
  307. $% [%new name=@t] :: new input draft
  308. $: %set-name
  309. draft-name=@t
  310. new-name=@t
  311. ==
  312. $: %add-input
  313. draft-name=@t
  314. input-name=@t
  315. ==
  316. $: %remove-input
  317. draft-name=@t
  318. input-name=@t
  319. ==
  320. $: %remove-input-by-name
  321. draft-name=@t
  322. name=[first=@t last=@t]
  323. ==
  324. [%print-status =draft-name] :: print draft status
  325. ==
  326. ::
  327. +$ npc-cause
  328. $% [%npc-bind pid=@ result=*]
  329. ==
  330. ::
  331. +$ effect
  332. $~ [%npc 0 %poke %fact *fact:dumb]
  333. $% file-effect
  334. [%markdown @t]
  335. [%raw *]
  336. [%npc pid=@ npc-effect]
  337. [%exit code=@]
  338. ==
  339. ::
  340. +$ file-effect
  341. $%
  342. [%file %read path=@t]
  343. [%file %write path=@t contents=@]
  344. ==
  345. ::
  346. +$ npc-effect
  347. $% [%poke $>(%fact cause:dumb)]
  348. [%peek path]
  349. ==
  350. ::
  351. ::TODO this probably shouldnt live in here
  352. ::
  353. ++ print
  354. |= nodes=markdown:m
  355. ^- (list effect)
  356. ~[(make-markdown-effect nodes)]
  357. ::
  358. ++ warn
  359. |* meg=tape
  360. |* *
  361. ?. bug +<
  362. ~> %slog.[1 (crip "WARN: !! {meg} !!")]
  363. +<
  364. ::
  365. ++ debug
  366. |* meg=tape
  367. |* *
  368. ?. bug +<
  369. ~> %slog.[0 (crip "wallet: {meg}")]
  370. +<
  371. ::
  372. ++ moat (keep state)
  373. ::
  374. ::
  375. :: +edit: modify inputs
  376. ++ edit
  377. |_ =state
  378. ::
  379. +* inp
  380. ^- preinput
  381. ?~ active-input.state
  382. %- (debug "no active input set!")
  383. *preinput
  384. =/ input-result (~(get-input plan draft-tree.state) u.active-input.state)
  385. ?~ input-result
  386. ~| "active input not found in draft-tree"
  387. !!
  388. u.input-result
  389. :: +add-seed: add a seed to the input
  390. ::
  391. ++ add-seed
  392. |= =seed:transact
  393. ^- [(list effect) ^state]
  394. ?: (~(has z-in:zo seeds.spend.p.inp) seed)
  395. :_ state
  396. %- print
  397. %- need
  398. %- de:md
  399. %- crip
  400. """
  401. ## add-seed
  402. **seed already exists in .spend**
  403. """
  404. =/ pre=preinput inp
  405. =/ =preinput
  406. %= pre
  407. seeds.spend.p
  408. %. seed
  409. ~(put z-in:zo seeds.spend.p.pre)
  410. ::
  411. seeds.spend.q %.y
  412. ==
  413. =. active-input.state (some name.pre)
  414. ::
  415. =/ input-name=input-name (need active-input.state)
  416. =. draft-tree.state
  417. (~(add-input plan draft-tree.state) input-name preinput)
  418. :: if active-seed is set, link it to this input
  419. =. draft-tree.state
  420. ?: ?=(^ active-seed.state)
  421. (~(link-seed-to-input plan draft-tree.state) input-name u.active-seed.state)
  422. draft-tree.state
  423. `state
  424. ::
  425. ++ remove-seed
  426. |= =seed:transact
  427. ^- [(list effect) ^state]
  428. ?. (~(has z-in:zo seeds.spend.p.inp) seed)
  429. :_ state
  430. %- print
  431. %- need
  432. %- de:md
  433. %- crip
  434. """
  435. ## remove-seed
  436. **seed not found in .spend**
  437. """
  438. =/ pre=preinput inp
  439. =. seeds.spend.p.pre
  440. %. seed
  441. ~(del z-in:zo seeds.spend.p.pre)
  442. =. draft-tree.state
  443. =/ input-name=input-name (need active-input.state)
  444. (~(add-input plan draft-tree.state) input-name pre)
  445. `state
  446. --
  447. ::
  448. :: +draw: modify drafts
  449. ++ draw
  450. |_ =state
  451. +* df
  452. ^- draft
  453. ?> ?=(^ active-draft.state)
  454. =/ draft-result (~(get-draft plan draft-tree.state) u.active-draft.state)
  455. ?~ draft-result
  456. *draft
  457. u.draft-result
  458. :: +add-input: add an input to the draft
  459. ::
  460. ++ add-input
  461. |= =input:transact
  462. ^- [(list effect) ^state]
  463. =/ =draft df
  464. =/ =input-name
  465. =+ (to-b58:nname:transact name.note.input)
  466. %- crip
  467. "{<first>}-{<last>}"
  468. ?: (~(has z-by:zo p.df) name.note.input)
  469. :_ state
  470. %- print
  471. %- need
  472. %- de:md
  473. %- crip
  474. """
  475. ## add-input
  476. **input already exists in .draft**
  477. draft already has input with note name: {<input-name>}
  478. """
  479. =/ active-draft=draft-name (need active-draft.state)
  480. =. p.draft
  481. %- ~(put z-by:zo p.draft)
  482. :- name.note.input
  483. input
  484. =. draft-tree.state
  485. %. [active-draft draft]
  486. ~(add-draft plan draft-tree.state)
  487. =. draft-tree.state
  488. %. [active-draft input-name]
  489. ~(link-input-to-draft plan draft-tree.state)
  490. write-draft
  491. ::
  492. ++ write-draft
  493. ^- [(list effect) ^state]
  494. =? active-draft.state ?=(~ active-draft.state) (some *draft-name)
  495. ?> ?=(^ active-draft.state)
  496. =/ =draft df
  497. =. draft-tree.state (~(add-draft plan draft-tree.state) u.active-draft.state draft)
  498. =/ dat-jam (jam draft)
  499. =/ path=@t (crip "drafts/{(trip u.active-draft.state)}.draft")
  500. =/ effect [%file %write path dat-jam]
  501. :_ state
  502. ~[effect [%exit 0]]
  503. --
  504. ::
  505. :: Convenience wrapper door for slip10 library
  506. :: ** Never use slip10 directly in the wallet **
  507. ++ s10
  508. |_ bas=base:slip10
  509. ++ gen-master-key
  510. |= [entropy=byts salt=byts]
  511. =/ argon-byts=byts
  512. :- 32
  513. %+ argon2-nockchain:argon2:crypto
  514. entropy
  515. salt
  516. =/ memo=tape (from-entropy:bip39 argon-byts)
  517. %- (debug "memo: {memo}")
  518. :- (crip memo)
  519. (from-seed:slip10 [64 (to-seed:bip39 memo "")])
  520. ::
  521. ++ from-seed
  522. |= =byts
  523. (from-seed:slip10 byts)
  524. ::
  525. ++ from-private
  526. |= =keyc:slip10
  527. (from-private:slip10 keyc)
  528. ::
  529. ++ from-public
  530. |= =keyc:slip10
  531. (from-public:slip10 keyc)
  532. ::
  533. :: Derives public key from parent public key
  534. :: index i is expected to be a bip32 style index
  535. :: meaning that for the n-th child key, i=n.
  536. ::
  537. ++ derive-public
  538. |= [parent=coil i=@u]
  539. ?> &(?=(%pub -.key.parent) (lte i (dec (bex 31))))
  540. => [cor=(from-public [p.key cc]:parent) i=i]
  541. (derive-public:cor i)
  542. ::
  543. :: Derives private key from parent private key
  544. :: index i is expected to be a bip32 style index
  545. :: meaning that for n-th child key: i = (n + 2^31)
  546. ::
  547. ++ derive-private
  548. |= [parent=coil i=@u]
  549. ?> &(?=(%prv -.key.parent) (gte i (bex 31)))
  550. => [cor=(from-private [p.key cc]:parent) i=i]
  551. (derive-private:cor i)
  552. --
  553. ::
  554. ++ vault
  555. |_ =state
  556. ++ base-path ^- trek
  557. ?~ master.state
  558. ~|("base path not accessible because master not set" !!)
  559. /keys/[t/(to-b58:master master.state)]
  560. ::
  561. ++ seed-path ^- trek
  562. (welp base-path /seed)
  563. ::
  564. ++ get
  565. |_ key-type=?(%pub %prv)
  566. ::
  567. ++ key-path ^- trek
  568. (welp base-path ~[key-type])
  569. ::
  570. ++ seed-path ^- trek
  571. (welp base-path /seed)
  572. ::
  573. ++ master
  574. ^- coil
  575. =/ =trek (welp key-path /m)
  576. =/ =meta (~(got of keys.state) trek)
  577. ?> ?=(%coil -.meta)
  578. meta
  579. ::
  580. ++ by-index
  581. |= index=@ud
  582. ^- coil
  583. ~| "key not found at index {<index>}"
  584. =/ =trek (welp key-path /[ud/index])
  585. =/ =meta (~(got of keys.state) trek)
  586. ?> ?=(%coil -.meta)
  587. meta
  588. ::
  589. ++ seed
  590. ^- meta
  591. ~| "key not found at {<seed-path>}"
  592. (~(got of keys.state) seed-path)
  593. ::
  594. ++ by-label
  595. |= label=@t
  596. ~| "key not found with label {<label>}"
  597. %+ murn keys
  598. |= [t=trek =meta]
  599. ?:(&(?=(%label -.meta) =(label +.meta)) `t ~)
  600. ::
  601. ++ keys
  602. ^- (list [trek meta])
  603. =/ subtree
  604. %- ~(kids of keys.state)
  605. key-path
  606. ~(tap by kid.subtree)
  607. ::
  608. ++ coils
  609. ^- (list coil)
  610. %+ murn keys
  611. |= [t=trek =meta]
  612. ^- (unit coil)
  613. ;; (unit coil)
  614. ?:(=(%coil -.meta) `meta ~)
  615. --
  616. ::
  617. ++ put
  618. |%
  619. ::
  620. ++ seed
  621. |= seed-phrase=@t
  622. ^- (axal meta)
  623. %- ~(put of keys.state)
  624. [seed-path [%seed seed-phrase]]
  625. ::
  626. ++ key
  627. |= [=coil index=(unit @) label=(unit @t)]
  628. ^- (axal meta)
  629. =/ key-type=@tas -.key.coil
  630. =/ suffix=trek
  631. ?@ index
  632. /[key-type]/m
  633. /[key-type]/[ud/u.index]
  634. =/ key-path=trek (welp base-path suffix)
  635. %- (debug "adding key at {(en-tape:trek key-path)}")
  636. =. keys.state (~(put of keys.state) key-path coil)
  637. ?~ label
  638. keys.state
  639. %+ ~(put of keys.state)
  640. (welp key-path /label)
  641. label/u.label
  642. --
  643. ::
  644. :: +derive-child: derives the i-th hardened/unhardened child
  645. ::
  646. :: derives the i-th hardened or unhardened child from the master key.
  647. :: this arm will convert i to fit the slip10/bip32 indexing schemes.
  648. :: the i-th hardened corresponds to index i + 2^31 while the ith
  649. :: unhardened child corresponds to index i.
  650. ::
  651. ++ derive-child
  652. |= [parent=coil i=@u]
  653. ^- coil
  654. ?: (gte i (bex 31))
  655. ~|("Child index {<i>} out of range. Child indices are capped to values between [0, 2^31)" !!)
  656. ?~ master.state
  657. ~|("No master keys available for derivation" !!)
  658. ?: ?=(%prv -.key.parent)
  659. ::
  660. :: If the parent key is %prv, then the child is hardened
  661. :: and we add 2^31 to the index
  662. => (derive-private:s10 parent (add i (bex 31)))
  663. [%coil [%prv private-key] chain-code]
  664. => (derive-public:s10 parent i)
  665. [%coil [%pub public-key] chain-code]
  666. ::
  667. ++ get-note
  668. |= name=nname:transact
  669. ^- nnote:transact
  670. ~| "note not found: {<name>}"
  671. (~(got z-by:zo balance.state) name)
  672. ::
  673. ++ get-note-from-hash
  674. |= has=hash:transact
  675. ^- nnote:transact
  676. =/ name=nname:transact
  677. (~(got z-by:zo hash-to-name.state) has)
  678. (get-note name)
  679. ::
  680. ++ generate-pid
  681. |= peek-type=?(%balance %block)
  682. ^- (unit @ud)
  683. =/ has-active-peek=?
  684. %- ~(any by peek-requests.state)
  685. |=(t=?(%balance %block) =(t peek-type))
  686. ?: has-active-peek ~
  687. =/ used-pids=(list @ud)
  688. ~(tap in ~(key by peek-requests.state))
  689. =/ max-pid=@ud
  690. (roll used-pids max)
  691. =/ next-pid=@ud +(max-pid)
  692. ?: =(next-pid 0) `1 :: handle wraparound
  693. `next-pid
  694. --
  695. :: +plan: core for managing draft relationships
  696. ::
  697. :: provides methods for adding, removing, and navigating the draft tree.
  698. :: uses the axal structure to maintain relationships between drafts, inputs,
  699. :: and seeds.
  700. ::
  701. ++ plan
  702. |_ tree=draft-tree
  703. ::
  704. :: +get-draft: retrieve a draft by name
  705. ::
  706. ++ get-draft
  707. |= name=draft-name
  708. ^- (unit draft)
  709. =/ res (~(get of tree) /draft/[name])
  710. ?~ res ~
  711. ?. ?=(%draft -.u.res) ~
  712. `draft.u.res
  713. :: +get-input: retrieve an input by name
  714. ::
  715. ++ get-input
  716. |= name=input-name
  717. ^- (unit preinput)
  718. =/ res (~(get of tree) /input/[name])
  719. ?~ res ~
  720. ?. ?=(%input -.u.res) ~
  721. `preinput.u.res
  722. :: +get-seed: retrieve a seed by name
  723. ::
  724. ++ get-seed
  725. |= name=seed-name
  726. ^- (unit preseed)
  727. =/ res (~(get of tree) /seed/[name])
  728. ?~ res ~
  729. ?. ?=(%seed -.u.res) ~
  730. `preseed.u.res
  731. :: +add-draft: add a new draft
  732. ::
  733. ++ add-draft
  734. |= [name=draft-name =draft]
  735. ^- draft-tree
  736. =/ entity [%draft name draft]
  737. (~(put of tree) /draft/[name] entity)
  738. :: +add-input: add a new input
  739. ::
  740. ++ add-input
  741. |= [name=input-name =preinput]
  742. ^- draft-tree
  743. =/ entity [%input name preinput]
  744. (~(put of tree) /input/[name] entity)
  745. :: +add-seed: add a new seed
  746. ::
  747. ++ add-seed
  748. |= [name=seed-name =preseed]
  749. ^- draft-tree
  750. =/ entity [%seed name preseed]
  751. (~(put of tree) /seed/[name] entity)
  752. :: +link-input-to-draft: link an input to a draft
  753. ::
  754. ++ link-input-to-draft
  755. |= [draft-name=draft-name input-name=input-name]
  756. ^- draft-tree
  757. =/ input-entity (~(get of tree) /input/[input-name])
  758. ?~ input-entity tree
  759. ?. ?=(%input -.u.input-entity) tree
  760. (~(put of tree) /draft/[draft-name]/input/[input-name] u.input-entity)
  761. :: +link-seed-to-input: link a seed to an input
  762. ::
  763. ++ link-seed-to-input
  764. |= [input-name=input-name seed-name=seed-name]
  765. ^- draft-tree
  766. =/ seed-entity (~(get of tree) /seed/[seed-name])
  767. ?~ seed-entity tree
  768. ?. ?=(%seed -.u.seed-entity) tree
  769. (~(put of tree) /input/[input-name]/seed/[seed-name] u.seed-entity)
  770. :: +unlink-input-from-draft: remove an input from a draft
  771. ::
  772. ++ unlink-input-from-draft
  773. |= [draft-name=draft-name input-name=input-name]
  774. ^- draft-tree
  775. (~(del of tree) /draft/[draft-name]/input/[input-name])
  776. :: +unlink-seed-from-input: remove a seed from an input
  777. ::
  778. ++ unlink-seed-from-input
  779. |= [input-name=input-name seed-name=seed-name]
  780. ^- draft-tree
  781. (~(del of tree) /input/[input-name]/seed/[seed-name])
  782. :: +list-draft-inputs: list all inputs in a draft
  783. ::
  784. ++ list-draft-inputs
  785. |= name=draft-name
  786. ^- (list input-name)
  787. =/ kids (~(kid of tree) /draft/[name])
  788. %+ murn ~(tap in ~(key by kids))
  789. |= pax=pith
  790. ^- (unit input-name)
  791. =/ pax=path (pout pax)
  792. ?> ?=([%input *] pax)
  793. ?> ?=(^ t.pax)
  794. `i.t.pax
  795. :: +list-input-seeds: list all seeds in an input
  796. ::
  797. ++ list-input-seeds
  798. |= name=input-name
  799. ^- (list seed-name)
  800. =/ kids (~(kid of tree) /input/[name])
  801. %+ murn ~(tap in ~(key by kids))
  802. |= pax=pith
  803. ^- (unit seed-name)
  804. =/ pax=path (pout pax)
  805. ?: &(?=([%seed *] pax) ?=(^ t.pax))
  806. `i.t.pax
  807. ~
  808. :: +list-all-drafts: list all draft names
  809. ::
  810. ++ list-all-drafts
  811. ^- (list draft-name)
  812. =/ kids (~(kid of tree) /draft)
  813. %+ murn ~(tap in ~(key by kids))
  814. |= pax=pith
  815. ^- (unit draft-name)
  816. =/ pax=path (pout pax)
  817. ?: ?=(^ pax)
  818. `i.pax
  819. ~
  820. :: +list-all-inputs: list all input names
  821. ::
  822. ++ list-all-inputs
  823. ^- (list input-name)
  824. =/ kids (~(kid of tree) /input)
  825. %+ murn ~(tap in ~(key by kids))
  826. |= pax=pith
  827. ^- (unit input-name)
  828. =/ pax=path (pout pax)
  829. ?: ?=(^ pax)
  830. `i.pax
  831. ~
  832. :: +list-all-seeds: list all seed names
  833. ::
  834. ++ list-all-seeds
  835. ^- (list seed-name)
  836. =/ kids (~(kid of tree) /seed)
  837. %+ murn ~(tap in ~(key by kids))
  838. |= pax=pith
  839. ^- (unit seed-name)
  840. =/ pax=path (pout pax)
  841. ?: &(?=([%seed *] pax) ?=(^ t.pax))
  842. `i.pax
  843. ~
  844. :: +remove-draft: completely remove a draft and its associations
  845. ::
  846. ++ remove-draft
  847. |= name=draft-name
  848. ^- draft-tree
  849. (~(lop of tree) /draft/[name])
  850. :: +remove-input: completely remove an input and its associations
  851. ::
  852. ++ remove-input
  853. |= name=input-name
  854. ^- draft-tree
  855. (~(lop of tree) /input/[name])
  856. :: +remove-seed: completely remove a seed and its associations
  857. ::
  858. ++ remove-seed
  859. |= name=seed-name
  860. ^- draft-tree
  861. (~(lop of tree) /seed/[name])
  862. --
  863. ::
  864. ++ make-markdown-effect
  865. |= nodes=markdown:m
  866. [%markdown (crip (en:md nodes))]
  867. ::
  868. ++ display-poke
  869. |= =cause
  870. ^- effect
  871. =/ nodes=markdown:m
  872. %- need
  873. %- de:md
  874. %- crip
  875. """
  876. ## poke
  877. {<cause>}
  878. """
  879. (make-markdown-effect nodes)
  880. ::
  881. ++ display-note
  882. |= note=nnote:transact
  883. ^- markdown:m
  884. %- need
  885. %- de:md
  886. %- crip
  887. """
  888. ## details
  889. - name: {<(to-b58:nname:transact name.note)>}
  890. - assets: {<assets.note>}
  891. - source: {<source.note>}
  892. ## lock
  893. - m: {<m.lock.note>}
  894. - signers: {<(to-b58:signers:lock:transact lock.note)>}
  895. """
  896. ::
  897. ++ show
  898. |= [=state =path]
  899. ^- [(list effect) ^state]
  900. |^
  901. ?+ path !!
  902. [%balance ~]
  903. :- ~[(display-balance balance.state)]
  904. state
  905. ::
  906. [%receive-address ~]
  907. :- (display-receive-address receive-address.state)
  908. state
  909. ::
  910. [%state ~]
  911. :- display-state
  912. state
  913. ==
  914. ++ display-balance
  915. |= =balance
  916. ^- effect
  917. =/ nodes=markdown:m
  918. %- need
  919. %- de:md
  920. %- crip
  921. """
  922. ## balance
  923. {<balance>}
  924. """
  925. (make-markdown-effect nodes)
  926. ::
  927. ++ display-receive-address
  928. |= address=lock:transact
  929. ^- (list effect)
  930. =/ nodes=markdown:m
  931. %- need
  932. %- de:md
  933. %- crip
  934. """
  935. ## receive address
  936. {<address>}
  937. """
  938. ~[(make-markdown-effect nodes)]
  939. ::
  940. ++ display-state
  941. ^- (list effect)
  942. =/ nodes=markdown:m
  943. %- need
  944. %- de:md
  945. %- crip
  946. """
  947. ## state
  948. - last block: {<last-block.state>}
  949. """
  950. ~[(make-markdown-effect nodes)]
  951. --
  952. --
  953. ::
  954. %- (moat |)
  955. ^- fort:moat
  956. |_ =state
  957. +* v ~(. vault state)
  958. d ~(. draw state)
  959. e ~(. edit state)
  960. p ~(. plan draft-tree.state)
  961. ::
  962. ++ load
  963. |= arg=^state
  964. ^- ^state
  965. arg
  966. ::
  967. ++ peek
  968. |= =path
  969. ^- (unit (unit *))
  970. %- (debug "peek: {<state>}")
  971. ?+ path ~
  972. ::
  973. [%balance ~]
  974. ``balance.state
  975. ::
  976. [%receive-address ~]
  977. ``receive-address.state
  978. ::
  979. [%state ~]
  980. ``state
  981. ==
  982. ::
  983. ++ poke
  984. |= =ovum:moat
  985. |^
  986. ^- [(list effect) ^state]
  987. %- (warn "debug printing may expose sensitive information")
  988. =/ =wire wire.ovum
  989. =+ [@ src=@ta ver=@ rest=*]=wire.ovum
  990. %- (debug "wire: {<[src ver `path`rest]>}")
  991. =/ cause=(unit cause)
  992. %- (soft cause)
  993. cause.input.ovum
  994. =/ success [%exit 0]
  995. =/ failure [%exit 1]
  996. ?~ cause
  997. %- (debug "input does not have a proper cause: {<cause.input.ovum>}")
  998. [~[failure] state]
  999. =/ cause u.cause
  1000. %- (debug "cause: {<-.cause>}")
  1001. =; process
  1002. =^ effs state process
  1003. :: check for pending balance commands and execute them
  1004. =^ pending-effs state handle-pending-commands
  1005. [(weld effs pending-effs) state]
  1006. ?- -.cause
  1007. %npc-bind (handle-npc cause)
  1008. %show (show state path.cause)
  1009. %keygen (do-keygen cause)
  1010. %derive-child (do-derive-child cause)
  1011. %sign-tx (do-sign-tx cause)
  1012. %scan (do-scan cause)
  1013. %list-notes (do-list-notes cause)
  1014. %list-notes-by-pubkey (do-list-notes-by-pubkey cause)
  1015. %simple-spend (do-simple-spend cause)
  1016. %update-balance (do-update-balance cause)
  1017. %update-block (do-update-block cause)
  1018. %import-keys (do-import-keys cause)
  1019. %import-master-pubkey (do-import-master-pubkey cause)
  1020. %gen-master-privkey (do-gen-master-privkey cause)
  1021. %gen-master-pubkey (do-gen-master-pubkey cause)
  1022. %make-tx (do-make-tx cause)
  1023. %list-pubkeys (do-list-pubkeys cause)
  1024. %show-balance (do-show-balance cause)
  1025. %sync-run (do-sync-run cause)
  1026. ::
  1027. %advanced-spend
  1028. ?- +<.cause
  1029. %seed (do-advanced-spend-seed +>.cause)
  1030. %input (do-advanced-spend-input +>.cause)
  1031. %draft (do-advanced-spend-draft +>.cause)
  1032. ==
  1033. ::
  1034. %file
  1035. ?> ?=(%write +<.cause)
  1036. ~& > "%file %write: {<cause>}"
  1037. [[%exit 0]~ state]
  1038. ==
  1039. ::
  1040. ++ handle-npc
  1041. |= =npc-cause
  1042. ^- [(list effect) ^state]
  1043. %- (debug "handle-npc: {<-.npc-cause>}")
  1044. ?- -.npc-cause
  1045. %npc-bind
  1046. =/ pid pid.npc-cause
  1047. =/ peek-type
  1048. ~| "no peek request found for pid: {<pid>}"
  1049. (~(got by peek-requests.state) pid)
  1050. =/ result result.npc-cause
  1051. ?- peek-type
  1052. %balance
  1053. =/ softed=(unit (unit (unit (z-map:zo nname:transact nnote:transact))))
  1054. %- (soft (unit (unit (z-map:zo nname:transact nnote:transact))))
  1055. result
  1056. =. peek-requests.state
  1057. (~(del by peek-requests.state) pid)
  1058. ?~ softed
  1059. %- (debug "handle-npc: %balance: could not soft result")
  1060. `state
  1061. =/ balance-result=(unit (unit _balance.state)) u.softed
  1062. ?~ balance-result
  1063. %- (warn "%update-balance did not return a result: bad path")
  1064. `state
  1065. ?~ u.balance-result
  1066. %- (warn "%update-balance did not return a result: nothing")
  1067. `state
  1068. ?~ u.u.balance-result
  1069. %- (warn "%update-balance did not return a result: empty result")
  1070. `state
  1071. =. balance.state u.u.balance-result
  1072. %- (debug "balance state updated!")
  1073. =. name-to-hash.state
  1074. %- ~(run z-by:zo balance.state)
  1075. |= not=nnote:transact
  1076. ^- hash:transact
  1077. (hash:nnote:transact not)
  1078. =. hash-to-name.state
  1079. %- ~(gas z-by:zo *(z-map:zo hash:transact nname:transact))
  1080. %+ turn ~(tap z-by:zo name-to-hash.state)
  1081. |= [name=nname:transact =hash:transact]
  1082. [hash name]
  1083. :: move each command from balance phase to ready phase
  1084. =/ balance-commands=(list [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]])
  1085. %+ skim ~(tap z-by:zo pending-commands.state)
  1086. |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
  1087. =(phase %balance)
  1088. =. pending-commands.state
  1089. %- ~(gas z-by:zo pending-commands.state)
  1090. %+ turn balance-commands
  1091. |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
  1092. [pid [%ready wrapped]]
  1093. ::
  1094. :: the top-level poke arm should check for pending commands
  1095. :: and execute them as appropriate
  1096. %- (debug "handle-npc: %balance: balance updated, pending commands ready for execution")
  1097. [~ state]
  1098. ::
  1099. %block
  1100. %- (debug "handle-npc: %block")
  1101. =/ softed=(unit (unit (unit (unit block-id:transact))))
  1102. %- (soft (unit (unit (unit block-id:transact))))
  1103. result
  1104. =. peek-requests.state
  1105. (~(del by peek-requests.state) pid)
  1106. ?~ softed
  1107. %- (warn "handle-npc: %block: could not soft result")
  1108. `state
  1109. =/ block-result u.softed
  1110. %- (debug "handle-npc: %block: {<block-result>}")
  1111. %- (debug "handle-npc: %block: {<peek-requests.state>}")
  1112. ?~ block-result
  1113. %- (warn "handle-npc: %block: bad path")
  1114. `state
  1115. ?~ u.block-result
  1116. %- (warn "handle-npc: %block: nothing")
  1117. `state
  1118. ?~ u.u.block-result
  1119. %- (warn "handle-npc: %block: empty result")
  1120. `state
  1121. %- (debug "handle-npc: %block: found block")
  1122. %- (debug "handle-npc: {<(to-b58:block-id:transact (need u.u.block-result))>}")
  1123. %- (debug "handle-npc: hash: {<(to-b58:hash:transact (need u.u.block-result))>}")
  1124. =. last-block.state u.u.block-result
  1125. :: move each command from block phase to balance phase
  1126. =/ block-commands=(list [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]])
  1127. %+ skim ~(tap z-by:zo pending-commands.state)
  1128. |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
  1129. =(phase %block)
  1130. ::
  1131. %- (debug "handle-npc: %block: preparing {<(lent block-commands)>} commands for balance update")
  1132. :: move each command to balance update phase
  1133. =. pending-commands.state
  1134. %- ~(gas z-by:zo pending-commands.state)
  1135. %+ turn block-commands
  1136. |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
  1137. [pid [%balance wrapped]]
  1138. :: check if we need to update balance (if there are commands waiting for it)
  1139. =/ have-balance-cmds=?
  1140. %- ~(any z-by:zo pending-commands.state)
  1141. |= [phase=?(%block %balance %ready) wrapped=*]
  1142. =(phase %balance)
  1143. ::
  1144. ?: have-balance-cmds
  1145. %- (debug "handle-npc: %block: initiating balance update for pending commands")
  1146. =^ balance-update-effs state
  1147. (do-update-balance [%update-balance ~])
  1148. [balance-update-effs state]
  1149. `state
  1150. ::
  1151. ==
  1152. ==
  1153. ::
  1154. ++ handle-pending-commands
  1155. ^- [(list effect) ^state]
  1156. =/ ready-commands=(list [pid=@ud =cause])
  1157. %+ turn
  1158. %+ skim ~(tap z-by:zo pending-commands.state)
  1159. |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
  1160. =(phase %ready)
  1161. |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
  1162. [pid wrapped]
  1163. ?~ ready-commands
  1164. %- (debug "no pending commands to execute")
  1165. `state
  1166. %- (debug "executing {<(lent ready-commands)>} pending commands")
  1167. :: process each command
  1168. =/ effs=(list effect) ~
  1169. =/ cmds=(list [pid=@ud =cause]) ready-commands
  1170. |-
  1171. ?~ cmds
  1172. [effs state]
  1173. =/ pid=@ud pid.i.cmds
  1174. =/ =cause +.i.cmds
  1175. =/ ov=^ovum
  1176. %* . ovum
  1177. cause.input cause
  1178. ==
  1179. =. pending-commands.state
  1180. (~(del z-by:zo pending-commands.state) pid)
  1181. ::
  1182. =^ cmd-effs state
  1183. (poke ov)
  1184. $(cmds t.cmds, effs (weld effs cmd-effs))
  1185. ::
  1186. ++ do-sync-run
  1187. |= =cause
  1188. ?> ?=(%sync-run -.cause)
  1189. %- (debug "sync-run: wrapped command {<-.wrapped.cause>}")
  1190. :: get a new pid for the command
  1191. =/ pid=(unit @ud) (generate-pid:v %block)
  1192. ?~ pid
  1193. :: if we can't get a pid, run the command directly
  1194. %- (debug "sync-run: no pid available, running command directly")
  1195. =/ ov=^ovum
  1196. %* . ovum
  1197. cause.input wrapped.cause
  1198. ==
  1199. (poke ov)
  1200. :: store the command in pending-commands
  1201. =. pending-commands.state
  1202. %+ ~(put z-by:zo pending-commands.state)
  1203. u.pid
  1204. [%block wrapped.cause]
  1205. :: initiate block update
  1206. =^ block-update-effs state
  1207. (do-update-block [%update-block ~])
  1208. :: return block update effects
  1209. [block-update-effs state]
  1210. ::
  1211. ++ do-update-balance
  1212. |= =cause
  1213. ?> ?=(%update-balance -.cause)
  1214. %- (debug "update-balance")
  1215. %- (debug "last balance size: {<(lent ~(tap z-by:zo balance.state))>}")
  1216. =/ pid=(unit @ud) (generate-pid:v %balance)
  1217. ?~ pid `state
  1218. =/ bid=block-id:transact
  1219. ~| "no last block found, not updating balance"
  1220. (need last-block.state)
  1221. =/ =path (snoc /balance (to-b58:block-id:transact bid))
  1222. =/ =effect [%npc u.pid %peek path]
  1223. :- ~[effect]
  1224. state(peek-requests (~(put by peek-requests.state) u.pid %balance))
  1225. ::
  1226. ++ do-update-block
  1227. |= =cause
  1228. ?> ?=(%update-block -.cause)
  1229. %- (debug "update-block")
  1230. %- (debug "last block: {<last-block.state>}")
  1231. =/ pid=(unit @ud) (generate-pid:v %block)
  1232. ?~ pid `state
  1233. =/ =path /heavy
  1234. =/ =effect [%npc u.pid %peek path]
  1235. :- ~[effect]
  1236. state(peek-requests (~(put by peek-requests.state) u.pid %block))
  1237. ::
  1238. ++ do-import-keys
  1239. |= =cause
  1240. ?> ?=(%import-keys -.cause)
  1241. =/ new-keys=_keys.state
  1242. %+ roll keys.cause
  1243. |= [[=trek =coil] acc=_keys.state]
  1244. (~(put of acc) trek coil)
  1245. `state(keys new-keys)
  1246. ::
  1247. ::>) TODO: update state.master as well
  1248. ++ do-import-master-pubkey
  1249. |= =cause
  1250. ?> ?=(%import-master-pubkey -.cause)
  1251. %- (debug "import-master-pubkey: {<key.cause>}")
  1252. =/ c=coil [%coil [%pub (de:base58:wrap (trip key.cause))] (de:base58:wrap (trip cc.cause))]
  1253. =/ cor (from-public:s10 [p.key.c cc.c])
  1254. =/ master-pubkey-coil=coil [%coil [%pub public-key] chain-code]:cor
  1255. =. master.state (some master-pubkey-coil)
  1256. =/ label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
  1257. %- (debug "Imported master public key: {<public-key:cor>}")
  1258. =. keys.state (key:put:v master-pubkey-coil ~ label)
  1259. `state
  1260. ::
  1261. ++ do-gen-master-privkey
  1262. |= =cause
  1263. ?> ?=(%gen-master-privkey -.cause)
  1264. :: We do not need to reverse the endian-ness of the seedphrase
  1265. :: because the bip39 code expects a tape.
  1266. =/ seed=byts [64 (to-seed:bip39 (trip seedphrase.cause) "")]
  1267. =/ cor (from-seed:s10 seed)
  1268. =/ master-pubkey-coil=coil [%coil [%pub public-key] chain-code]:cor
  1269. =/ master-privkey-coil=coil [%coil [%prv private-key] chain-code]:cor
  1270. =. master.state (some master-pubkey-coil)
  1271. =/ public-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
  1272. =/ private-label `(crip "master-private-{<(end [3 4] public-key:cor)>}")
  1273. =. keys.state (key:put:v master-privkey-coil ~ private-label)
  1274. =. keys.state (key:put:v master-pubkey-coil ~ public-label)
  1275. =. keys.state (seed:put:v seedphrase.cause)
  1276. %- (debug "master.state: {<master.state>}")
  1277. [[%exit 0]~ state]
  1278. ::
  1279. ++ do-gen-master-pubkey
  1280. |= =cause
  1281. ?> ?=(%gen-master-pubkey -.cause)
  1282. =/ cor (from-private:s10 master-privkey.cause)
  1283. =/ master-pubkey-coil=coil [%coil [%pub public-key] chain-code]:cor
  1284. =/ master-privkey-coil=coil [%coil [%prv private-key] chain-code]:cor
  1285. %- (debug "Generated master public key: {<public-key:cor>}")
  1286. =/ public-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
  1287. =/ private-label `(crip "master-private-{<(end [3 4] public-key:cor)>}")
  1288. =. master.state (some master-pubkey-coil)
  1289. =. keys.state (key:put:v master-privkey-coil ~ private-label)
  1290. =. keys.state (key:put:v master-pubkey-coil ~ public-label)
  1291. %- (debug "master.state: {<master.state>}")
  1292. `state
  1293. ::
  1294. ++ do-make-tx
  1295. |= =cause
  1296. ?> ?=(%make-tx -.cause)
  1297. %- (debug "make-tx: {<dat.cause>}")
  1298. :: note that new:raw-tx calls +validate already
  1299. =/ raw=raw-tx:transact (new:raw-tx:transact p.dat.cause)
  1300. =/ tx-id id.raw
  1301. =/ nock-cause=$>(%fact cause:dumb)
  1302. [%fact %heard-tx raw]
  1303. %- (debug "make-tx: new-tx {<tx-id>}")
  1304. :_ state
  1305. :~
  1306. [%npc 0 %poke nock-cause]
  1307. [%exit 0]
  1308. ==
  1309. ::
  1310. ++ do-list-pubkeys
  1311. |= =cause
  1312. ?> ?=(%list-pubkeys -.cause)
  1313. =/ pubkeys ~(coils get:v %pub)
  1314. =/ base58-keys=(list tape)
  1315. %+ turn pubkeys
  1316. |= =coil
  1317. =/ pubkey=schnorr-pubkey:transact pub:(from-public:s10 [p.key cc]:coil)
  1318. %- trip
  1319. (to-b58:schnorr-pubkey:transact pubkey)
  1320. %- (debug "base58-keys: {<base58-keys>}")
  1321. :- [%exit 0]~
  1322. state
  1323. ::
  1324. ++ do-show-balance
  1325. |= =cause
  1326. ?> ?=(%show-balance -.cause)
  1327. %- (debug "show-balance: {<block.cause>}")
  1328. =/ =path /balance/[(scot %ux block.cause)]
  1329. =/ =effect [%npc 0 %peek path]
  1330. :- ~[effect]
  1331. state
  1332. ::
  1333. ++ do-scan
  1334. |= =cause
  1335. ?> ?=(%scan -.cause)
  1336. %- (debug "scan: scanning {<search-depth.cause>} addresses")
  1337. ~| "something went wrong! state: {<state>}"
  1338. ?> ?=(^ master.state)
  1339. :: get all public keys up to search depth
  1340. =/ index=@ud search-depth.cause
  1341. =/ coils=(list coil)
  1342. =/ keys=(list coil) [~(master get:v %pub)]~
  1343. =| done=_|
  1344. |- ^- (list coil)
  1345. ?: done keys
  1346. =? done =(0 index) &
  1347. =/ base=trek /keys/pub/[ux/p.key:(public:master master.state)]/[ud/index]
  1348. =/ key=(unit coil)
  1349. ;; (unit coil)
  1350. (~(get of keys.state) base)
  1351. %= $
  1352. index ?:(=(0 index) 0 (dec index))
  1353. keys ?^(key (snoc keys u.key) keys)
  1354. ==
  1355. :: fail when no coils
  1356. ~| "no coils for master key"
  1357. ?< ?=(~ coils)
  1358. :: generate first names of notes owned by each pubkey
  1359. =/ first-names=(list [hash:transact schnorr-pubkey:transact])
  1360. %+ turn coils
  1361. |= =coil
  1362. :: create lock from public key
  1363. =/ pubkey=schnorr-pubkey:transact pub:(from-public:s10 [p.key cc]:coil)
  1364. =/ =lock:transact (new:lock:transact pubkey)
  1365. :: generate name and take first name
  1366. =/ match-name=nname:transact
  1367. %- new:nname:transact
  1368. :* lock
  1369. [*hash:transact %.n] :: empty source, not a coinbase
  1370. *timelock:transact :: no timelock
  1371. ==
  1372. [-.match-name pubkey]
  1373. :: find notes with matching first names in balance
  1374. =/ notes=(list nnote:transact)
  1375. %+ murn
  1376. ~(tap z-by:zo balance.state)
  1377. |= [name=nname:transact note=nnote:transact]
  1378. ^- (unit nnote:transact)
  1379. :: check if first name matches any in our list
  1380. =/ matches
  1381. %+ lien first-names
  1382. |= [first-name=hash:transact pubkey=schnorr-pubkey:transact]
  1383. =/ =lock:transact (new:lock:transact pubkey)
  1384. :: update lock if include-multisig is true and pubkey is in
  1385. :: the multisig set in the note's lock
  1386. =? lock
  1387. ?& include-multisig.cause
  1388. (~(has z-in:zo pubkeys.lock.note) pubkey)
  1389. ==
  1390. lock.note
  1391. :: update match-name if include-timelocks is set
  1392. =? first-name include-timelocks.cause
  1393. =< -
  1394. %- new:nname:transact
  1395. :* lock
  1396. [*hash:transact %.n] :: empty source, not a coinbase
  1397. timelock.note :: include timelock
  1398. ==
  1399. =(-.name first-name)
  1400. ?:(matches `note ~)
  1401. %- (debug "found matches: {<notes>}")
  1402. =/ nodes=markdown:m
  1403. :~ :- %leaf
  1404. :- %heading
  1405. :* %atx 1
  1406. :~ [%text 'Scan Result']
  1407. ==
  1408. ==
  1409. :- %container
  1410. :- %ul
  1411. :* 0 '*'
  1412. (turn notes display-note)
  1413. ==
  1414. ==
  1415. :_ state
  1416. ~[(make-markdown-effect nodes)]
  1417. ::
  1418. ++ do-list-notes
  1419. |= =cause
  1420. ?> ?=(%list-notes -.cause)
  1421. %- (debug "list-notes")
  1422. =/ notes=(list [name=nname:transact note=nnote:transact])
  1423. %+ sort ~(tap z-by:zo balance.state)
  1424. |= $: a=[name=nname:transact note=nnote:transact]
  1425. b=[name=nname:transact note=nnote:transact]
  1426. ==
  1427. (gth assets.note.a assets.note.b)
  1428. =/ nodes=markdown:m
  1429. %+ welp
  1430. %- need
  1431. %- de:md
  1432. %- crip
  1433. """
  1434. ## wallet notes
  1435. """
  1436. %- zing
  1437. %+ turn notes
  1438. |= [* =nnote:transact]
  1439. (display-note nnote)
  1440. :_ state
  1441. ~[(make-markdown-effect nodes) [%exit 0]]
  1442. ::
  1443. ++ do-list-notes-by-pubkey
  1444. |= =cause
  1445. ~& "list-notes-by-pubkey"
  1446. ?> ?=(%list-notes-by-pubkey -.cause)
  1447. ~& "list-notes-by-pubkey: {<pubkey.cause>}"
  1448. =/ target-pubkey=schnorr-pubkey:transact
  1449. (from-b58:schnorr-pubkey:transact pubkey.cause)
  1450. =/ matching-notes=(list [name=nname:transact note=nnote:transact])
  1451. %+ skim ~(tap z-by:zo balance.state)
  1452. |= [name=nname:transact note=nnote:transact]
  1453. (~(has z-in:zo pubkeys.lock.note) target-pubkey)
  1454. =/ sorted-notes=(list [name=nname:transact note=nnote:transact])
  1455. %+ sort matching-notes
  1456. |= $: a=[name=nname:transact note=nnote:transact]
  1457. b=[name=nname:transact note=nnote:transact]
  1458. ==
  1459. (gth assets.note.a assets.note.b)
  1460. =/ nodes=markdown:m
  1461. %+ welp
  1462. %- need
  1463. %- de:md
  1464. %- crip
  1465. """
  1466. ## wallet notes for pubkey {<(to-b58:schnorr-pubkey:transact target-pubkey)>}
  1467. """
  1468. %- zing
  1469. %+ turn sorted-notes
  1470. |= [* =nnote:transact]
  1471. (display-note nnote)
  1472. :_ state
  1473. ~[(make-markdown-effect nodes) [%raw sorted-notes] [%exit 0]]
  1474. ::
  1475. ++ do-simple-spend
  1476. |= =cause
  1477. ?> ?=(%simple-spend -.cause)
  1478. %- (debug "simple-spend: {<names.cause>}")
  1479. :: for now, each input corresponds to a single name and recipient. all
  1480. :: assets associated with the name are transferred to the recipient.
  1481. ::
  1482. :: thus there is one recipient per name, and one seed per recipient.
  1483. ::
  1484. =/ names=(list nname:transact)
  1485. %+ turn names.cause
  1486. |= [first=@t last=@t]
  1487. ~| "could not parse names: {<names.cause>}"
  1488. (from-b58:nname:transact [first last])
  1489. =/ recipients=(list lock:transact)
  1490. %+ turn recipients.cause
  1491. |= [m=@ pks=(list @t)]
  1492. %+ m-of-n:new:lock:transact m
  1493. %- ~(gas z-in:zo *(z-set:zo schnorr-pubkey:transact))
  1494. %+ turn pks
  1495. |= pk=@t
  1496. ~| "could not parse recipients: {<recipients.cause>}"
  1497. (from-b58:schnorr-pubkey:transact pk)
  1498. ::
  1499. =/ gifts=(list coins:transact) gifts.cause
  1500. ::
  1501. ~| "different number of names/recipients/gifts"
  1502. ?> ?& =((lent names) (lent recipients))
  1503. =((lent names) (lent gifts))
  1504. ==
  1505. =| ledger=(list [name=nname:transact recipient=lock:transact gifts=coins:transact])
  1506. =. ledger
  1507. |-
  1508. ?~ names ledger
  1509. :: since we assert they are equal in length above, this is just to get
  1510. :: the i face
  1511. ?~ gifts ledger
  1512. ?~ recipients ledger
  1513. %= $
  1514. ledger [[i.names i.recipients i.gifts] ledger]
  1515. names t.names
  1516. gifts t.gifts
  1517. recipients t.recipients
  1518. ==
  1519. ::
  1520. :: the fee is subtracted from the first note that permits doing so without overspending
  1521. =/ fee=coins:transact fee.cause
  1522. :: use the first private key to construct the subsequent inputs
  1523. =/ sender=coil
  1524. =/ private-keys=(list coil) ~(coils get:v %prv)
  1525. ?~ private-keys
  1526. ~|("No private keys available for signing" !!)
  1527. (head private-keys)
  1528. =/ sender-key=schnorr-seckey:transact
  1529. (from-atom:schnorr-seckey:transact p.key.sender)
  1530. :: for each name, create an input from the corresponding note in sender's
  1531. :: balance at the current block. the fee will be subtracted entirely from
  1532. :: the first note that has sufficient assets for both the fee and the gift.
  1533. :: the refund is sent to receive-address.state
  1534. =/ [ins=(list input:transact) spent-fee=?]
  1535. %^ spin ledger `?`%.n
  1536. |= $: $: name=nname:transact
  1537. recipient=lock:transact
  1538. gift=coins:transact
  1539. ==
  1540. spent-fee=?
  1541. ==
  1542. =/ note=nnote:transact (get-note:v name)
  1543. ?: (gth gift assets.note)
  1544. ~| "gift {<gift>} larger than assets {<assets.note>} for recipient {<recipient>}"
  1545. !!
  1546. ?: ?& !spent-fee
  1547. (lte (add gift fee) assets.note)
  1548. ==
  1549. :: we can subtract the fee from this note
  1550. :_ %.y
  1551. %- with-choice:with-refund:simple-from-note:new:input:transact
  1552. [recipient gift fee note sender-key receive-address.state]
  1553. :: :: we cannot subtract the fee from this note, or we already have from a previous one
  1554. :_ %.n
  1555. %- with-choice:with-refund:simple-from-note:new:input:transact
  1556. [recipient gift 0 note sender-key receive-address.state]
  1557. ::
  1558. ?. spent-fee
  1559. ~|("no note suitable to subtract fee from, aborting operation" !!)
  1560. =/ ins-draft=inputs:transact (multi:new:inputs:transact ins)
  1561. ~| "last-block unknown!"
  1562. ?> ?=(^ last-block.state)
  1563. :: name is the b58-encoded name of the first input
  1564. =/ draft-name=@t
  1565. %- head
  1566. %+ turn ~(tap z-by:zo (names:inputs:transact ins-draft))
  1567. |= =nname:transact
  1568. =< last
  1569. (to-b58:nname:transact nname)
  1570. :: jam inputs and save as draft
  1571. =/ =draft
  1572. %* . *draft
  1573. p ins-draft
  1574. name draft-name
  1575. ==
  1576. =/ draft-jam (jam draft)
  1577. =/ path=@t
  1578. %- crip
  1579. "./drafts/{(trip name.draft)}.draft"
  1580. %- (debug "saving draft to {<path>}")
  1581. =/ =effect [%file %write path draft-jam]
  1582. :- ~[effect [%exit 0]]
  1583. state
  1584. ::
  1585. ++ do-keygen
  1586. |= =cause
  1587. ?> ?=(%keygen -.cause)
  1588. =+ [seed-phrase=@t cor]=(gen-master-key:s10 entropy.cause salt.cause)
  1589. =/ master-public-coil [%coil [%pub public-key] chain-code]:cor
  1590. =/ master-private-coil [%coil [%prv private-key] chain-code]:cor
  1591. =. master.state (some master-public-coil)
  1592. %- (debug "keygen: new public key {<public-key:cor>}")
  1593. %- (debug "keygen: public key: base58 {<(en:base58:wrap public-key:cor)>}")
  1594. %- (debug "keygen: new private key {<private-key:cor>}")
  1595. %- (debug "keygen: private key: base58 {<(en:base58:wrap private-key:cor)>}")
  1596. =/ pub-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
  1597. =/ prv-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
  1598. =. keys.state (key:put:v master-public-coil ~ pub-label)
  1599. =. keys.state (key:put:v master-private-coil ~ prv-label)
  1600. =. keys.state (seed:put:v seed-phrase)
  1601. :- [%exit 0]~
  1602. state
  1603. ::
  1604. :: derives child %pub or %prv key of current master key
  1605. :: at index `i`. this will overwrite existing paths.
  1606. ++ do-derive-child
  1607. |= =cause
  1608. ?> ?=(%derive-child -.cause)
  1609. =/ par=coil ~(master get:v key-type.cause)
  1610. =/ child=coil (derive-child:v par i.cause)
  1611. =. keys.state
  1612. (key:put:v child `i.cause label.cause)
  1613. :- [%exit 0]~
  1614. state
  1615. ::
  1616. ++ do-sign-tx
  1617. |= =cause
  1618. ?> ?=(%sign-tx -.cause)
  1619. %- (debug "sign-tx: {<dat.cause>}")
  1620. :: get private key at specified index, or first derived key if no index
  1621. =/ private-keys=(list coil) ~(coils get:v %prv)
  1622. ?~ private-keys
  1623. ~|("No private keys available for signing" !!)
  1624. =/ sender=coil
  1625. ?~ index.cause i.private-keys
  1626. =/ key-at-index=meta (~(by-index get:v %prv) u.index.cause)
  1627. ?> ?=(%coil -.key-at-index)
  1628. key-at-index
  1629. =/ sender-key=schnorr-seckey:transact
  1630. (from-atom:schnorr-seckey:transact p.key.sender)
  1631. =/ signed-inputs=inputs:transact
  1632. %- ~(run z-by:zo p.dat.cause)
  1633. |= =input:transact
  1634. %- (debug "signing input: {<input>}")
  1635. =. spend.input
  1636. %+ sign:spend:transact
  1637. spend.input
  1638. sender-key
  1639. input
  1640. =/ signed-draft=draft
  1641. %= dat.cause
  1642. p signed-inputs
  1643. ==
  1644. =/ draft-jam (jam signed-draft)
  1645. =/ path=@t
  1646. %- crip
  1647. "./drafts/{(trip name.signed-draft)}.draft"
  1648. %- (debug "saving input draft to {<path>}")
  1649. =/ =effect [%file %write path draft-jam]
  1650. :- ~[effect [%exit 0]]
  1651. state
  1652. ::
  1653. ++ do-advanced-spend-seed
  1654. |= cause=advanced-spend-seed
  1655. ^- [(list effect) ^state]
  1656. |^
  1657. =? active-draft.state ?=(~ active-draft.state) `*draft-name
  1658. =? active-seed.state ?=(~ active-seed.state) `*seed-name
  1659. ?- -.cause
  1660. %new do-new
  1661. %set-name do-set-name
  1662. %set-source do-set-source
  1663. %set-recipient do-set-recipient
  1664. %set-timelock do-set-timelock
  1665. %set-gift do-set-gift
  1666. %set-parent-hash do-set-parent-hash
  1667. %set-parent-hash-from-name do-set-parent-hash-from-name
  1668. %print-status do-print-status
  1669. ==
  1670. ::
  1671. ++ do-new
  1672. ?> ?=([%new *] cause)
  1673. =/ sed=preseed
  1674. %* . *preseed
  1675. name name.cause
  1676. ==
  1677. (write-seed sed)
  1678. ::
  1679. ++ do-set-name
  1680. ?> ?=([%set-name *] cause)
  1681. =/ pre=(unit preseed)
  1682. (get-seed:p seed-name.cause)
  1683. ?> ?=(^ pre)
  1684. =. name.u.pre new-name.cause
  1685. (write-seed u.pre)
  1686. ::
  1687. ++ do-set-source
  1688. ?> ?=([%set-source *] cause)
  1689. =/ pre=(unit preseed)
  1690. (get-seed:p seed-name.cause)
  1691. ?> ?=(^ pre)
  1692. =/ sed=preseed
  1693. ?~ source.cause
  1694. %= u.pre
  1695. output-source.p ~
  1696. output-source.q %.y
  1697. ==
  1698. %= u.pre
  1699. output-source.p (some (from-b58:source:transact u.source.cause))
  1700. output-source.q %.y
  1701. ==
  1702. (write-seed sed)
  1703. ::
  1704. ++ do-set-recipient
  1705. ?> ?=([%set-recipient *] cause)
  1706. =/ pre=(unit preseed)
  1707. (get-seed:p seed-name.cause)
  1708. ?> ?=(^ pre)
  1709. =/ recipient=lock:transact
  1710. %+ m-of-n:new:lock:transact m.recipient.cause
  1711. %- ~(gas z-in:zo *(z-set:zo schnorr-pubkey:transact))
  1712. (turn pks.recipient.cause from-b58:schnorr-pubkey:transact)
  1713. =/ sed=preseed
  1714. %= u.pre
  1715. recipient.p recipient
  1716. recipient.q %.y
  1717. ==
  1718. (write-seed sed)
  1719. ::
  1720. ++ do-set-timelock
  1721. ?> ?=([%set-timelock *] cause)
  1722. ::TODO
  1723. !!
  1724. ::
  1725. ++ do-set-gift
  1726. ?> ?=([%set-gift *] cause)
  1727. =/ pre=(unit preseed)
  1728. (get-seed:p seed-name.cause)
  1729. ?> ?=(^ pre)
  1730. =/ sed=preseed
  1731. %= u.pre
  1732. gift.p gift.cause
  1733. gift.q %.y
  1734. ==
  1735. (write-seed sed)
  1736. ::
  1737. ++ do-set-parent-hash
  1738. ?> ?=([%set-parent-hash *] cause)
  1739. =/ pre=(unit preseed)
  1740. (get-seed:p seed-name.cause)
  1741. ?> ?=(^ pre)
  1742. =/ sed=preseed
  1743. %= u.pre
  1744. parent-hash.q %.y
  1745. parent-hash.p (from-b58:hash:transact parent-hash.cause)
  1746. ==
  1747. (write-seed sed)
  1748. ::
  1749. ++ do-set-parent-hash-from-name
  1750. ?> ?=([%set-parent-hash-from-name *] cause)
  1751. =/ pre=(unit preseed)
  1752. (get-seed:p seed-name.cause)
  1753. ?> ?=(^ pre)
  1754. =/ name=nname:transact (from-b58:nname:transact name.cause)
  1755. =/ not=nnote:transact (get-note:v name)
  1756. =/ sed=preseed
  1757. %= u.pre
  1758. parent-hash.p (hash:nnote:transact not)
  1759. parent-hash.q %.y
  1760. ==
  1761. (write-seed sed)
  1762. ::
  1763. ++ do-print-status
  1764. ?> ?=([%print-status *] cause)
  1765. =/ pre=(unit preseed)
  1766. (get-seed:p seed-name.cause)
  1767. ?> ?=(^ pre)
  1768. =/ output-source-text=tape
  1769. ?: !output-source.q.u.pre
  1770. "Unset (any output source is OK)"
  1771. <output-source.p.u.pre>
  1772. =/ recipient-text=tape
  1773. ?: !recipient.q.u.pre
  1774. "Unset"
  1775. <recipient.p.u.pre>
  1776. =/ timelock-text=tape
  1777. ?: !timelock-intent.q.u.pre
  1778. "Unset (no intent)"
  1779. <timelock-intent.p.u.pre>
  1780. =/ gift-text=tape
  1781. ?: !gift.q.u.pre
  1782. "Gift: unset (gift must be nonzero)"
  1783. ?: =(0 gift.p.u.pre)
  1784. """
  1785. Gift: 0 (must be nonzero)
  1786. """
  1787. """
  1788. Gift: {<gift.p.u.pre>}
  1789. """
  1790. =/ status-text=tape
  1791. """
  1792. ## Seed Status
  1793. ### Output Source
  1794. {output-source-text}
  1795. ### Recipient
  1796. {recipient-text}
  1797. ### Timelock Intent
  1798. {timelock-text}
  1799. ### Gift
  1800. {gift-text}
  1801. """
  1802. :_ state
  1803. (print (need (de:md (crip status-text))))
  1804. ::
  1805. ++ write-seed
  1806. |= sed=preseed
  1807. ^- [(list effect) ^state]
  1808. =. active-seed.state (some name.sed)
  1809. =. draft-tree.state (~(add-seed plan draft-tree.state) name.sed sed)
  1810. =^ writes state write-draft:d
  1811. [writes state]
  1812. -- ::+do-advanced-spend-seed
  1813. ::
  1814. ++ do-advanced-spend-input
  1815. |= cause=advanced-spend-input
  1816. ^- [(list effect) ^state]
  1817. |^
  1818. ?- -.cause
  1819. %new do-new
  1820. %set-name do-set-name
  1821. %add-seed do-add-seed
  1822. %set-fee do-set-fee
  1823. %set-note-from-name do-set-note-from-name
  1824. %set-note-from-hash do-set-note-from-hash
  1825. %derive-note-from-seeds do-derive-note-from-seeds
  1826. %remove-seed do-remove-seed
  1827. %remove-seed-by-hash do-remove-seed-by-hash
  1828. %print-status do-print-status
  1829. ==
  1830. ::
  1831. ++ do-new
  1832. ?> ?=([%new *] cause)
  1833. =/ inp=preinput
  1834. %* . *preinput
  1835. name name.cause
  1836. ==
  1837. =. active-input.state (some name.cause)
  1838. (write-input inp)
  1839. ::
  1840. ++ do-set-name
  1841. ?> ?=([%set-name *] cause)
  1842. =/ pre=(unit preinput)
  1843. (get-input:p input-name.cause)
  1844. ?> ?=(^ pre)
  1845. =. name.u.pre new-name.cause
  1846. =. active-input.state (some new-name.cause)
  1847. (write-input u.pre)
  1848. ::
  1849. ++ do-add-seed
  1850. ?> ?=([%add-seed *] cause)
  1851. ::
  1852. =/ pre=(unit preinput)
  1853. (get-input:p input-name.cause)
  1854. ?> ?=(^ pre)
  1855. =/ sed=(unit preseed)
  1856. (get-seed:p seed-name.cause)
  1857. ?> ?=(^ sed)
  1858. ?: (~(has z-in:zo seeds.spend.p.u.pre) p.u.sed)
  1859. :_ state
  1860. =/ nodes=markdown:m
  1861. :~ :- %leaf
  1862. :- %paragraph
  1863. :~ [%text (crip "seed already exists in .spend, doing nothing.")]
  1864. ==
  1865. ==
  1866. (print nodes)
  1867. =/ inp=preinput
  1868. %= u.pre
  1869. seeds.spend.p (~(put z-in:zo seeds.spend.p.u.pre) p.u.sed)
  1870. seeds.spend.q %.y
  1871. ==
  1872. (write-input inp)
  1873. ::
  1874. ++ do-set-fee
  1875. ?> ?=([%set-fee *] cause)
  1876. =/ pre=(unit preinput)
  1877. (get-input:p input-name.cause)
  1878. ?> ?=(^ pre)
  1879. =. fee.spend.p.u.pre fee.cause
  1880. =. fee.spend.q.u.pre %.y
  1881. (write-input u.pre)
  1882. ::
  1883. ++ do-set-note-from-name
  1884. ?> ?=([%set-note-from-name *] cause)
  1885. ::
  1886. =/ pre=(unit preinput)
  1887. (get-input:p input-name.cause)
  1888. ?> ?=(^ pre)
  1889. =/ name=nname:transact (from-b58:nname:transact name.cause)
  1890. =/ not=nnote:transact (get-note:v name)
  1891. =/ inp=preinput
  1892. %= u.pre
  1893. note.p not
  1894. note.q %.y
  1895. ==
  1896. (write-input inp)
  1897. ::
  1898. ++ do-set-note-from-hash
  1899. ?> ?=([%set-note-from-hash *] cause)
  1900. ::
  1901. =/ pre=(unit preinput)
  1902. (get-input:p input-name.cause)
  1903. ?> ?=(^ pre)
  1904. =/ =hash:transact (from-b58:hash:transact hash.cause)
  1905. =/ note=nnote:transact (get-note-from-hash:v hash)
  1906. =/ inp=preinput
  1907. %= u.pre
  1908. note.p note
  1909. note.q %.y
  1910. ==
  1911. (write-input inp)
  1912. ::
  1913. ++ do-derive-note-from-seeds
  1914. ?> ?=([%derive-note-from-seeds *] cause)
  1915. ::
  1916. =/ pre=(unit preinput)
  1917. (get-input:p input-name.cause)
  1918. ?> ?=(^ pre)
  1919. =/ seeds-list=(list seed:transact)
  1920. ~(tap z-in:zo seeds.spend.p.u.pre)
  1921. ?~ seeds-list
  1922. :_ state
  1923. =/ nodes=markdown:m
  1924. :~ :- %leaf
  1925. :- %paragraph
  1926. :~ [%text (crip "no seeds exist in .spend, so note cannot be set.")]
  1927. ==
  1928. ==
  1929. (print nodes)
  1930. =/ =hash:transact parent-hash.i.seeds-list
  1931. =/ note=nnote:transact (get-note-from-hash:v hash)
  1932. =/ inp=preinput
  1933. %= u.pre
  1934. note.p note
  1935. note.q %.y
  1936. ==
  1937. (write-input inp)
  1938. ::
  1939. ++ do-remove-seed
  1940. ?> ?=([%remove-seed *] cause)
  1941. ::
  1942. =/ pre=(unit preinput)
  1943. (get-input:p input-name.cause)
  1944. ?> ?=(^ pre)
  1945. =. active-input.state (some input-name.cause)
  1946. =/ sed=(unit preseed)
  1947. (get-seed:p seed-name.cause)
  1948. ?> ?=(^ sed)
  1949. ?: !(~(has z-in:zo seeds.spend.p.u.pre) p.u.sed)
  1950. :_ state
  1951. =/ nodes=markdown:m
  1952. :~ :- %leaf
  1953. :- %paragraph
  1954. :~ [%text (crip "seed does not exist in .spend, doing nothing")]
  1955. ==
  1956. ==
  1957. (print nodes)
  1958. =/ inp=preinput
  1959. %= u.pre
  1960. seeds.spend.p (~(del z-in:zo seeds.spend.p.u.pre) p.u.sed)
  1961. seeds.spend.q !=(*seeds:transact seeds.spend.p.u.pre)
  1962. ==
  1963. (write-input inp)
  1964. ::
  1965. ++ do-remove-seed-by-hash
  1966. ?> ?=([%remove-seed-by-hash *] cause)
  1967. :: find seed with hash
  1968. =/ pre=(unit preinput)
  1969. (get-input:p input-name.cause)
  1970. ?> ?=(^ pre)
  1971. =. active-input.state (some input-name.cause)
  1972. =/ seed-hashes=(z-map:zo hash:transact seed:transact)
  1973. %- ~(gas z-by:zo *(z-map:zo hash:transact seed:transact))
  1974. %+ turn ~(tap z-in:zo seeds.spend.p.u.pre)
  1975. |= sed=seed:transact
  1976. [(hash:seed:transact sed) sed]
  1977. =/ has=hash:transact (from-b58:hash:transact hash.cause)
  1978. ?. (~(has z-by:zo seed-hashes) has)
  1979. :_ state
  1980. =/ nodes=markdown:m
  1981. :~ :- %leaf
  1982. :- %paragraph
  1983. :~ [%text (crip "seed does not exist in .spend, doing nothing")]
  1984. ==
  1985. ==
  1986. (print nodes)
  1987. =/ remove-seed=seed:transact
  1988. (~(got z-by:zo seed-hashes) has)
  1989. ::
  1990. =/ inp=preinput
  1991. %= u.pre
  1992. seeds.spend.p (~(del z-in:zo seeds.spend.p.u.pre) remove-seed)
  1993. seeds.spend.q !=(*seeds:transact seeds.spend.p.u.pre)
  1994. ==
  1995. (write-input inp)
  1996. ::
  1997. ++ do-print-status
  1998. ?> ?=([%print-status *] cause)
  1999. =/ pre=(unit preinput)
  2000. (get-input:p input-name.cause)
  2001. ?> ?=(^ pre)
  2002. =| status-nodes=markdown:m
  2003. =. status-nodes
  2004. %+ snoc status-nodes
  2005. :- %leaf
  2006. :- %paragraph
  2007. ?: !signature.spend.q.u.pre
  2008. :: TODO we removed the ability to sign in this control flow
  2009. [%text (crip ".signature: unset")]~
  2010. :: check the signature
  2011. ::
  2012. :: get a .parent-hash of a seed. they have to all be the same, so which
  2013. :: one doesn't matter; if they're not all the same validation will fail.
  2014. =/ seeds-list=(list seed:transact)
  2015. ~(tap z-in:zo seeds.spend.p.u.pre)
  2016. ?~ seeds-list
  2017. [%text (crip "no seeds exist, so signature cannot be checked")]~
  2018. =/ parent-note-hash=hash:transact parent-hash.i.seeds-list
  2019. =/ parent-note-hash-b58=tape
  2020. (trip (to-b58:hash:transact parent-note-hash))
  2021. =/ parent-note-name=(unit nname:transact)
  2022. (~(get z-by:zo hash-to-name.state) parent-note-hash)
  2023. ?~ parent-note-name
  2024. :~
  2025. :- %text
  2026. %- crip
  2027. """
  2028. note with hash {parent-note-hash-b58} present in .spend but
  2029. has no matching .name in wallet
  2030. """
  2031. :- %text
  2032. :: TODO better, more succint error message.
  2033. '''
  2034. this implies that it is not in the balance unless there is a hash collision.
  2035. please report this as a bug if you are sure you have the $note, as this
  2036. situation is very unlkely. the spend ought to still be valid in that case
  2037. and you can broadcast it anyway.
  2038. '''
  2039. ==
  2040. =/ parent-note-name-b58=tape
  2041. =; [first=@t last=@t]
  2042. "<(trip first)> <(trip last)>"
  2043. (to-b58:nname:transact u.parent-note-name)
  2044. =/ parent-note=(unit nnote:transact)
  2045. (~(get z-by:zo balance.state) u.parent-note-name)
  2046. ?~ parent-note
  2047. :~
  2048. :- %text
  2049. %- crip
  2050. """
  2051. note with name {parent-note-name-b58} and hash {parent-note-hash-b58}
  2052. present in .spend but not in balance
  2053. """
  2054. ==
  2055. ?: (verify:spend:transact spend.p.u.pre u.parent-note)
  2056. [%text (crip "signature(s) on spend are valid.")]~
  2057. :: missing or invalid sigs
  2058. =/ have-sigs
  2059. %+ turn
  2060. %~ tap z-in:zo
  2061. ^- (z-set:zo schnorr-pubkey:transact)
  2062. %~ key z-by:zo (need signature.spend.p.u.pre)
  2063. |= pk=schnorr-pubkey:transact
  2064. [%text (to-b58:schnorr-pubkey:transact pk)]
  2065. ?~ have-sigs
  2066. [%text 'no signatures found!']~
  2067. =/ lock-b58=[m=@ pks=(list @t)]
  2068. (to-b58:lock:transact recipient.i.seeds-list)
  2069. =/ need-sigs
  2070. (turn pks.lock-b58 (lead %text))
  2071. ?~ need-sigs
  2072. [%text 'no recipients found!']~
  2073. ;: welp
  2074. :~ [%text (crip "signature on spend did not validate.")]
  2075. [%text (crip "signatures on spend:")]
  2076. ==
  2077. ::TODO check if any particular signature did not validate
  2078. have-sigs
  2079. :~ [%text (crip ".lock on parent note:")]
  2080. [%text (crip "number of sigs required: {(scow %ud m.lock-b58)}")]
  2081. [%text (crip "pubkeys of possible signers:")]
  2082. ==
  2083. need-sigs
  2084. ==
  2085. ::TODO check individual seeds? this would require some refactoring and
  2086. ::the happy path does not involve adding unfinished seeds to an input.
  2087. :_ state
  2088. (print status-nodes)
  2089. ::
  2090. ++ write-input
  2091. |= inp=preinput
  2092. ^- [(list effect) ^state]
  2093. =. active-input.state (some name.inp)
  2094. =. draft-tree.state (~(add-input plan draft-tree.state) name.inp inp)
  2095. =^ writes state write-draft:d
  2096. [writes state]
  2097. -- ::+do-advanced-spend-input
  2098. ::
  2099. ++ do-advanced-spend-draft
  2100. |= cause=advanced-spend-draft
  2101. ^- [(list effect) ^state]
  2102. |^
  2103. =? active-draft.state ?=(~ active-draft.state) `*draft-name
  2104. ?- -.cause
  2105. %new do-new
  2106. %set-name do-set-name
  2107. %add-input do-add-input
  2108. %remove-input do-remove-input
  2109. %remove-input-by-name do-remove-input-by-name
  2110. %print-status do-print-status
  2111. ==
  2112. ::
  2113. ++ do-new
  2114. ?> ?=([%new *] cause)
  2115. =. active-draft.state (some name.cause)
  2116. =/ dat=draft
  2117. %* . *draft
  2118. name name.cause
  2119. ==
  2120. (write-draft dat)
  2121. ::
  2122. ++ do-set-name
  2123. ?> ?=([%set-name *] cause)
  2124. =/ pre=(unit draft)
  2125. (get-draft:p draft-name.cause)
  2126. ?> ?=(^ pre)
  2127. =. active-draft.state (some new-name.cause)
  2128. =. name.u.pre new-name.cause
  2129. (write-draft u.pre)
  2130. ::
  2131. ++ do-add-input
  2132. ?> ?=([%add-input *] cause)
  2133. =/ pre=(unit draft)
  2134. (get-draft:p draft-name.cause)
  2135. ?> ?=(^ pre)
  2136. =. active-draft.state (some draft-name.cause)
  2137. =/ inp=(unit preinput)
  2138. (get-input:p input-name.cause)
  2139. ?> ?=(^ inp)
  2140. ?: (~(has z-by:zo p.u.pre) name.note.p.u.inp)
  2141. :_ state
  2142. %- print
  2143. ^- markdown:m
  2144. :_ ~ :- %leaf
  2145. :- %paragraph
  2146. :_ ~ :- %text
  2147. %- crip
  2148. """
  2149. draft already has input with note name
  2150. {<(to-b58:nname:transact name.note.p.u.inp)>}, doing nothing.
  2151. """
  2152. =. p.u.pre
  2153. (~(put z-by:zo p.u.pre) [name.note.p.u.inp p.u.inp])
  2154. (write-draft u.pre)
  2155. ::
  2156. ++ do-remove-input
  2157. ?> ?=([%remove-input *] cause)
  2158. =/ pre=(unit draft)
  2159. (get-draft:p draft-name.cause)
  2160. ?> ?=(^ pre)
  2161. =. active-draft.state (some draft-name.cause)
  2162. =/ inp=(unit preinput)
  2163. (get-input:p input-name.cause)
  2164. ?> ?=(^ inp)
  2165. ?. (~(has z-by:zo p.u.pre) name.note.p.u.inp)
  2166. :_ state
  2167. %- print
  2168. :_ ~ :- %leaf
  2169. :- %paragraph
  2170. :_ ~ :- %text
  2171. %- crip
  2172. """
  2173. draft does not have input with note name
  2174. {<(to-b58:nname:transact name.note.p.u.inp)>}, doing nothing.
  2175. """
  2176. ?. =(u.inp (~(got z-by:zo p.u.pre) name.note.p.u.inp))
  2177. :_ state
  2178. %- print
  2179. :_ ~ :- %leaf
  2180. :- %paragraph
  2181. :_ ~ :- %text
  2182. %- crip
  2183. """
  2184. draft has input with note name
  2185. {<(to-b58:nname:transact name.note.p.u.inp)>}, but it is
  2186. a different input. to remove this input, use %remove-input-by-name
  2187. instead.
  2188. """
  2189. =. p.u.pre
  2190. (~(del z-by:zo p.u.pre) name.note.p.u.inp)
  2191. (write-draft u.pre)
  2192. ::
  2193. ++ do-remove-input-by-name
  2194. ?> ?=([%remove-input-by-name *] cause)
  2195. =/ pre=(unit draft)
  2196. (get-draft:p draft-name.cause)
  2197. =. active-draft.state (some draft-name.cause)
  2198. ?> ?=(^ pre)
  2199. =/ name=nname:transact (from-b58:nname:transact name.cause)
  2200. ?. (~(has z-by:zo p.u.pre) name)
  2201. :_ state
  2202. %- print
  2203. :_ ~ :- %leaf
  2204. :- %paragraph
  2205. :_ ~ :- %text
  2206. %- crip
  2207. """
  2208. draft does not have input with note name {(trip first.name.cause)}
  2209. {(trip last.name.cause)}, doing nothing.
  2210. """
  2211. =. p.u.pre (~(del z-by:zo p.u.pre) name)
  2212. (write-draft u.pre)
  2213. ::
  2214. ++ do-print-status
  2215. ?> ?=([%print-status *] cause)
  2216. =/ pre=(unit draft)
  2217. (get-draft:p draft-name.cause)
  2218. =. active-draft.state (some draft-name.cause)
  2219. ?> ?=(^ pre)
  2220. =/ inputs=(list [name=nname:transact =input:transact])
  2221. ~(tap z-by:zo p.u.pre)
  2222. =/ input-texts=(list tape)
  2223. %+ turn inputs
  2224. |= [name=nname:transact =input:transact]
  2225. =/ signature-text=tape ?~(signature.spend.input "unset" "set")
  2226. =/ name-text=tape <(to-b58:nname:transact name)>
  2227. =/ note-text=tape <(to-b58:hash:transact (hash:nnote:transact note.input))>
  2228. =/ seeds-text=tape
  2229. %- zing
  2230. %+ turn ~(tap z-in:zo seeds.spend.input)
  2231. |= =seed:transact
  2232. """
  2233. - recipient: {<(to-b58:lock:transact recipient.seed)>}
  2234. - gift: {<gift.seed>}
  2235. - parent hash: {<(to-b58:hash:transact parent-hash.seed)>}
  2236. """
  2237. """
  2238. #### Input {name-text}:
  2239. - Note hash: {note-text}
  2240. - Fee: {<fee.spend.input>}
  2241. - Signature: {signature-text}
  2242. ##### Seeds
  2243. {seeds-text}
  2244. """
  2245. =/ status-text=tape
  2246. """
  2247. ## Draft Status
  2248. Name: {(trip name.u.pre)}
  2249. Number of inputs: {<(lent inputs)>}
  2250. ### Inputs
  2251. {(zing input-texts)}
  2252. """
  2253. :_ state
  2254. (print (need (de:md (crip status-text))))
  2255. ::
  2256. ++ write-draft
  2257. |= dat=draft
  2258. ^- [(list effect) ^state]
  2259. =. active-draft.state (some name.dat)
  2260. write-draft:d
  2261. -- ::+do-advanced-spend-draft
  2262. -- ::+poke
  2263. --