wallet.hoon 69 KB

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