| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342 |
- :: /ker/wallet/wallet: nockchain wallet
- /= bip39 /common/bip39
- /= slip10 /common/slip10
- /= m /common/markdown/types
- /= md /common/markdown/markdown
- /= transact /common/tx-engine
- /= z /common/zeke
- /= zo /common/zoon
- /= dumb /apps/dumbnet/lib/types
- /= * /common/zose
- /= * /common/wrapper
- ::
- =>
- =| bug=_&
- |%
- :: $key: public or private key
- ::
- :: a public key is generated from a private key and +cc (chain code).
- :: pub contains the base58 encoded version of the cheetah curve point
- ::
- +$ key
- $~ [%pub p=*@ux]
- $% [%pub p=@ux]
- [%prv p=@ux]
- ==
- :: $coil: key and chaincode
- ::
- :: a wallet consists of a collection of +coil (address and entropy pair). the
- :: $cc (called a chain code elsewhere) allows for the deterministic
- :: generation of child keys from a parent key without compromising other
- :: branches of the hierarchy.
- ::
- :: .key: public or private key
- :: .cc: associated entropy (chain code)
- ::
- +$ coil [%coil =key =cc]
- ::
- :: $meta: stored metadata for a key
- +$ meta
- $% coil
- [%label @t]
- [%seed @t]
- ==
- ::
- :: $keys: path indexed map for keys
- ::
- :: path format for keys state:
- ::
- :: /keys :: root path (holds nothing in its fil)
- :: /keys/[t/master]/[key-type]/m/[coil/key] :: master key path
- :: /keys/[t/master]/[key-type]/[ud/index]/[coil/key] :: derived key path
- :: /keys/[t/master]/[key-type]/[ud/index]/[coil/key] :: specific key path
- :: /keys/[t/master]/[key-type]/[ud/index]/label/[label/label] :: key label path for derived key
- :: /keys/[t/master]/[key-type]/m/label/[label/label] :: key label path for master key
- :: /keys/[t/master]/seed/[seed/seed-phrase] :: seed-phrase path
- ::
- :: Note the terminal entry of the path holds that value, this value is the
- :: non-unit `fil` in the $axal definition
- ::
- :: where:
- :: - [t/master] is the base58 encoded master public key as @t
- :: - m denotes the master key
- :: - [ud/index] is the derivation index as @ud
- :: - [key-type] is either %pub or %prv
- :: - [coil/key] is the key and chaincode pair. key is in serialized
- :: format as a @ux, NOT base58.
- :: - [seed/seed-phrase] is the seed phrase as a tape
- :: - [label/label] is a label value
- ::
- :: master key is stored under 'm'.
- :: derived keys use incrementing indices starting from 0 under their master-key and key-type
- :: labels are stored as children of their associated keys.
- :: seed is a seed phrase and is only stored as a child of [t/master]
- ::
- +$ keys $+(keys-axal (axal meta))
- ::
- :: $draft-tree: structured tree of draft, input, and seed data
- ::
- :: we use the axal structure to track the relationship between drafts,
- :: inputs, and seeds. this allows us to navigate the tree and maintain
- :: all the relationships without duplicating data.
- ::
- :: paths in the draft-tree follow these conventions:
- ::
- :: /draft/[draft-name] :: draft node
- :: /draft/[draft-name]/input/[input-name] :: input in a draft
- :: /input/[input-name] :: input node
- :: /input/[input-name]/seed/[seed-name] :: seed in an input
- :: /seed/[seed-name] :: seed node
- ::
- +$ draft-tree
- $+ wallet-draft-tree
- (axal draft-entity)
- :: $draft-entity: entities stored in the draft tree
- ::
- +$ draft-entity
- $% [%draft =draft-name =draft]
- [%input =input-name =preinput]
- [%seed =seed-name =preseed]
- ==
- ::
- :: +master: master key pair
- ++ master
- =< form
- |%
- +$ form (unit coil)
- ++ public
- |= =form
- ?: ?=(^ form)
- u.form
- ~|("master public key not found" !!)
- ::
- ++ to-b58
- |= =form
- ^- @t
- (crip (en:base58:wrap p.key:(public form)))
- --
- :: $cc: chaincode
- ::
- +$ cc @ux
- :: $balance: wallet balance
- +$ balance
- $+ wallet-balance
- (z-map:zo nname:transact nnote:transact)
- :: $state: wallet state
- ::
- +$ state
- $: %0
- =balance
- hash-to-name=(z-map:zo hash:transact nname:transact) :: hash of note -> name of note
- name-to-hash=(z-map:zo nname:transact hash:transact) :: name of note -> hash of note
- receive-address=lock:transact
- =master
- =keys
- transactions=$+(transactions (map * transaction))
- last-block=(unit block-id:transact)
- peek-requests=$+(peek-requests (map @ud ?(%balance %block)))
- active-draft=(unit draft-name)
- active-input=(unit input-name)
- active-seed=(unit seed-name) :: currently selected seed
- draft-tree=draft-tree :: structured tree of drafts, inputs, and seeds
- pending-commands=(z-map:zo @ud [phase=?(%block %balance %ready) wrapped=cause]) :: commands waiting for sync
- ==
- +$ seed-name $~('default-seed' @t)
- ::
- +$ draft-name $~('default-draft' @t)
- ::
- +$ input-name $~('default-input' @t)
- ::
- :: $transaction: TODO
- ::
- +$ transaction
- $: recipient=@ux
- amount=@ud
- status=?(%unsigned %signed %sent)
- ==
- ::
- +$ cause
- $% [%keygen entropy=byts salt=byts]
- [%derive-child key-type=?(%pub %prv) i=@ label=(unit @t)]
- [%import-keys keys=(list (pair trek coil))]
- [%import-master-pubkey key=@t cc=@t] :: base58-encoded pubkey + chain code
- [%make-tx dat=draft]
- [%list-notes-by-pubkey pubkey=@t] :: base58-encoded pubkey
- $: %simple-spend
- names=(list [first=@t last=@t]) :: base58-encoded name hashes
- recipients=(list [m=@ pks=(list @t)]) :: base58-encoded locks
- gifts=(list coins:transact) :: number of coins to spend
- fee=coins:transact :: fee
- ==
- [%sign-tx dat=draft index=(unit @ud) entropy=@]
- [%list-pubkeys ~]
- [%list-notes ~]
- [%show-seedphrase ~]
- [%show-master-pubkey ~]
- [%show-master-privkey ~]
- [%show =path]
- [%gen-master-privkey seedphrase=@t]
- [%gen-master-pubkey master-privkey=keyc:slip10]
- [%update-balance ~]
- [%update-block ~]
- [%sync-run wrapped=cause] :: run command after sync completes
- $: %scan
- master-pubkey=@t :: base58 encoded master public key to scan for
- search-depth=$~(100 @ud) :: how many addresses to scan (default 100)
- include-timelocks=$~(%.n ?) :: include timelocked notes (default false)
- include-multisig=$~(%.n ?) :: include notes with multisigs (default false)
- ==
- [%advanced-spend advanced-spend]
- [%file %write path=@t contents=@t success=?]
- npc-cause
- ==
- ::
- +$ advanced-spend
- $% [%seed advanced-spend-seed]
- [%input advanced-spend-input]
- [%draft advanced-spend-draft]
- ==
- ::
- +$ advanced-spend-seed
- $% [%new name=@t] :: new empty seed in draft
- $: %set-name
- seed-name=@t
- new-name=@t
- ==
- $: %set-source :: set .output-source
- seed-name=@t
- source=(unit [hash=@t is-coinbase=?])
- ==
- $: %set-recipient :: set .recipient
- seed-name=@t
- recipient=[m=@ pks=(list @t)]
- ==
- $: %set-timelock :: set .timelock-intent
- seed-name=@t
- absolute=timelock-range:transact
- relative=timelock-range:transact
- ==
- $: %set-gift
- seed-name=@t
- gift=coins:transact
- ==
- $: %set-parent-hash
- seed-name=@t
- parent-hash=@t
- ==
- $: %set-parent-hash-from-name
- seed-name=@t
- name=[@t @t]
- ==
- $: %print-status :: do the needful
- seed-name=@t
- ==
- ==
- :: $seed-mask: tracks which fields of a $seed:transact have been set
- ::
- :: this might have been better as a "unitized seed" but would have been
- :: much more annoying to read the code
- +$ seed-mask
- $~ [%.n %.n %.n %.n %.n]
- $: output-source=?
- recipient=?
- timelock-intent=?
- gift=?
- parent-hash=?
- ==
- :: $preseed: a $seed:transact in process of being built
- +$ preseed [name=@t (pair seed:transact seed-mask)]
- ::
- :: $spend-mask: tracks which field of a $spend:transact have been set
- +$ spend-mask
- $~ [%.n %.n %.n]
- $: signature=?
- seeds=?
- fee=?
- ==
- ::
- +$ advanced-spend-input
- :: there is only one right way to create an $input from a $spend, so we don't need
- :: the mask or other commands.
- $% [%new name=@t] :: new empty input
- $: %set-name
- input-name=@t
- new-name=@t
- ==
- $: %add-seed
- input-name=@t
- seed-name=@t
- ==
- $: %set-fee
- input-name=@t
- fee=coins:transact
- ==
- $: %set-note-from-name :: set .note using .name
- input-name=@t
- name=[@t @t]
- ==
- $: %set-note-from-hash :: set .note using hash
- input-name=@t
- hash=@t
- ==
- $: %derive-note-from-seeds :: derive note from seeds
- input-name=@t
- ==
- $: %remove-seed
- input-name=@t
- seed-name=@t
- ==
- $: %remove-seed-by-hash
- input-name=@t
- hash=@t
- ==
- $: %print-status
- input-name=@t
- ==
- ==
- ::
- +$ input-mask
- $~ [%.n *spend-mask]
- $: note=?
- spend=spend-mask
- ==
- ::
- +$ preinput [name=@t (pair input:transact input-mask)]
- ::
- +$ draft [name=@t p=inputs:transact]
- ::
- +$ advanced-spend-draft
- $% [%new name=@t] :: new input draft
- $: %set-name
- draft-name=@t
- new-name=@t
- ==
- $: %add-input
- draft-name=@t
- input-name=@t
- ==
- $: %remove-input
- draft-name=@t
- input-name=@t
- ==
- $: %remove-input-by-name
- draft-name=@t
- name=[first=@t last=@t]
- ==
- [%print-status =draft-name] :: print draft status
- ==
- ::
- +$ npc-cause
- $% [%npc-bind pid=@ result=*]
- ==
- ::
- +$ effect
- $~ [%npc 0 %poke %fact *fact:dumb]
- $% file-effect
- [%markdown @t]
- [%raw *]
- [%npc pid=@ npc-effect]
- [%exit code=@]
- ==
- ::
- +$ file-effect
- $%
- [%file %read path=@t]
- [%file %write path=@t contents=@]
- ==
- ::
- +$ npc-effect
- $% [%poke $>(%fact cause:dumb)]
- [%peek path]
- ==
- ::
- ::TODO this probably shouldnt live in here
- ::
- ++ print
- |= nodes=markdown:m
- ^- (list effect)
- ~[(make-markdown-effect nodes)]
- ::
- ++ warn
- |* meg=tape
- |* *
- ?. bug +<
- ~> %slog.[1 (crip "WARN: !! {meg} !!")]
- +<
- ::
- ++ debug
- |* meg=tape
- |* *
- ?. bug +<
- ~> %slog.[2 (crip "wallet: {meg}")]
- +<
- ::
- ++ moat (keep state)
- ::
- ::
- :: +edit: modify inputs
- ++ edit
- |_ =state
- ::
- +* inp
- ^- preinput
- ?~ active-input.state
- %- (debug "no active input set!")
- *preinput
- =/ input-result (~(get-input plan draft-tree.state) u.active-input.state)
- ?~ input-result
- ~|("active input not found in draft-tree" !!)
- u.input-result
- :: +add-seed: add a seed to the input
- ::
- ++ add-seed
- |= =seed:transact
- ^- [(list effect) ^state]
- ?: (~(has z-in:zo seeds.spend.p.inp) seed)
- :_ state
- %- print
- %- need
- %- de:md
- %- crip
- """
- ## add-seed
- **seed already exists in .spend**
- """
- =/ pre=preinput inp
- =/ =preinput
- %= pre
- seeds.spend.p
- %. seed
- ~(put z-in:zo seeds.spend.p.pre)
- ::
- seeds.spend.q %.y
- ==
- =. active-input.state (some name.pre)
- ::
- =/ input-name=input-name (need active-input.state)
- =. draft-tree.state
- (~(add-input plan draft-tree.state) input-name preinput)
- :: if active-seed is set, link it to this input
- =. draft-tree.state
- ?: ?=(^ active-seed.state)
- (~(link-seed-to-input plan draft-tree.state) input-name u.active-seed.state)
- draft-tree.state
- `state
- ::
- ++ remove-seed
- |= =seed:transact
- ^- [(list effect) ^state]
- ?. (~(has z-in:zo seeds.spend.p.inp) seed)
- :_ state
- %- print
- %- need
- %- de:md
- %- crip
- """
- ## remove-seed
- **seed not found in .spend**
- """
- =/ pre=preinput inp
- =. seeds.spend.p.pre
- %. seed
- ~(del z-in:zo seeds.spend.p.pre)
- =. draft-tree.state
- =/ input-name=input-name (need active-input.state)
- (~(add-input plan draft-tree.state) input-name pre)
- `state
- --
- ::
- :: +draw: modify drafts
- ++ draw
- |_ =state
- +* df
- ^- draft
- ?> ?=(^ active-draft.state)
- =/ draft-result (~(get-draft plan draft-tree.state) u.active-draft.state)
- ?~ draft-result
- *draft
- u.draft-result
- :: +add-input: add an input to the draft
- ::
- ++ add-input
- |= =input:transact
- ^- [(list effect) ^state]
- =/ =draft df
- =/ =input-name
- =+ (to-b58:nname:transact name.note.input)
- %- crip
- "{<first>}-{<last>}"
- ?: (~(has z-by:zo p.df) name.note.input)
- :_ state
- %- print
- %- need
- %- de:md
- %- crip
- """
- ## add-input
- **input already exists in .draft**
- draft already has input with note name: {<input-name>}
- """
- =/ active-draft=draft-name (need active-draft.state)
- =. p.draft
- %- ~(put z-by:zo p.draft)
- :- name.note.input
- input
- =. draft-tree.state
- %. [active-draft draft]
- ~(add-draft plan draft-tree.state)
- =. draft-tree.state
- %. [active-draft input-name]
- ~(link-input-to-draft plan draft-tree.state)
- write-draft
- ::
- ++ write-draft
- ^- [(list effect) ^state]
- =? active-draft.state ?=(~ active-draft.state) (some *draft-name)
- ?> ?=(^ active-draft.state)
- =/ =draft df
- =. draft-tree.state (~(add-draft plan draft-tree.state) u.active-draft.state draft)
- =/ dat-jam (jam draft)
- =/ path=@t (crip "drafts/{(trip u.active-draft.state)}.draft")
- =/ effect [%file %write path dat-jam]
- :_ state
- ~[effect [%exit 0]]
- --
- ::
- :: Convenience wrapper door for slip10 library
- :: ** Never use slip10 directly in the wallet **
- ++ s10
- |_ bas=base:slip10
- ++ gen-master-key
- |= [entropy=byts salt=byts]
- =/ argon-byts=byts
- :- 32
- %+ argon2-nockchain:argon2:crypto
- entropy
- salt
- =/ memo=tape (from-entropy:bip39 argon-byts)
- %- (debug "memo: {memo}")
- :- (crip memo)
- (from-seed:slip10 [64 (to-seed:bip39 memo "")])
- ::
- ++ from-seed
- |= =byts
- (from-seed:slip10 byts)
- ::
- ++ from-private
- |= =keyc:slip10
- (from-private:slip10 keyc)
- ::
- ++ from-public
- |= =keyc:slip10
- (from-public:slip10 keyc)
- ::
- :: Derives public key from parent public key
- :: index i is expected to be a bip32 style index
- :: meaning that for the n-th child key, i=n.
- ::
- ++ derive-public
- |= [parent=coil i=@u]
- ?> &(?=(%pub -.key.parent) (lte i (dec (bex 31))))
- => [cor=(from-public [p.key cc]:parent) i=i]
- (derive-public:cor i)
- ::
- :: Derives private key from parent private key
- :: index i is expected to be a bip32 style index
- :: meaning that for n-th child key: i = (n + 2^31)
- ::
- ++ derive-private
- |= [parent=coil i=@u]
- ?> &(?=(%prv -.key.parent) (gte i (bex 31)))
- => [cor=(from-private [p.key cc]:parent) i=i]
- (derive-private:cor i)
- --
- ::
- ++ vault
- |_ =state
- ++ base-path ^- trek
- ?~ master.state
- ~|("base path not accessible because master not set" !!)
- /keys/[t/(to-b58:master master.state)]
- ::
- ++ seed-path ^- trek
- (welp base-path /seed)
- ::
- ++ get
- |_ key-type=?(%pub %prv)
- ::
- ++ key-path ^- trek
- (welp base-path ~[key-type])
- ::
- ++ seed-path ^- trek
- (welp base-path /seed)
- ::
- ++ master
- ^- coil
- =/ =trek (welp key-path /m)
- =/ =meta (~(got of keys.state) trek)
- ?> ?=(%coil -.meta)
- meta
- ::
- ++ by-index
- |= index=@ud
- ^- coil
- =/ =trek (welp key-path /[ud/index])
- =/ =meta (~(got of keys.state) trek)
- ?> ?=(%coil -.meta)
- meta
- ::
- ++ seed
- ^- meta
- (~(got of keys.state) seed-path)
- ::
- ++ by-label
- |= label=@t
- %+ murn keys
- |= [t=trek =meta]
- ?:(&(?=(%label -.meta) =(label +.meta)) `t ~)
- ::
- ++ keys
- ^- (list [trek meta])
- =/ subtree
- %- ~(kids of keys.state)
- key-path
- ~(tap by kid.subtree)
- ::
- ++ coils
- ^- (list coil)
- %+ murn keys
- |= [t=trek =meta]
- ^- (unit coil)
- ;; (unit coil)
- ?:(=(%coil -.meta) `meta ~)
- --
- ::
- ++ put
- |%
- ::
- ++ seed
- |= seed-phrase=@t
- ^- (axal meta)
- %- ~(put of keys.state)
- [seed-path [%seed seed-phrase]]
- ::
- ++ key
- |= [=coil index=(unit @) label=(unit @t)]
- ^- (axal meta)
- =/ key-type=@tas -.key.coil
- =/ suffix=trek
- ?@ index
- /[key-type]/m
- /[key-type]/[ud/u.index]
- =/ key-path=trek (welp base-path suffix)
- %- (debug "adding key at {(en-tape:trek key-path)}")
- =. keys.state (~(put of keys.state) key-path coil)
- ?~ label
- keys.state
- %+ ~(put of keys.state)
- (welp key-path /label)
- label/u.label
- --
- ::
- :: +derive-child: derives the i-th hardened/unhardened child
- ::
- :: derives the i-th hardened or unhardened child from the master key.
- :: this arm will convert i to fit the slip10/bip32 indexing schemes.
- :: the i-th hardened corresponds to index i + 2^31 while the ith
- :: unhardened child corresponds to index i.
- ::
- ++ derive-child
- |= [parent=coil i=@u]
- ^- coil
- ?: (gte i (bex 31))
- ~|("Child index {<i>} out of range. Child indices are capped to values between [0, 2^31)" !!)
- ?~ master.state
- ~|("No master keys available for derivation" !!)
- ?: ?=(%prv -.key.parent)
- ::
- :: If the parent key is %prv, then the child is hardened
- :: and we add 2^31 to the index
- => (derive-private:s10 parent (add i (bex 31)))
- [%coil [%prv private-key] chain-code]
- => (derive-public:s10 parent i)
- [%coil [%pub public-key] chain-code]
- ::
- ++ get-note
- |= name=nname:transact
- ^- nnote:transact
- ?: (~(has z-by:zo balance.state) name)
- (~(got z-by:zo balance.state) name)
- ~|("note not found: {<name>}" !!)
- ::
- ++ get-note-from-hash
- |= has=hash:transact
- ^- nnote:transact
- =/ name=nname:transact
- (~(got z-by:zo hash-to-name.state) has)
- (get-note name)
- ::
- ++ generate-pid
- |= peek-type=?(%balance %block)
- ^- (unit @ud)
- =/ has-active-peek=?
- %- ~(any by peek-requests.state)
- |=(t=?(%balance %block) =(t peek-type))
- ?: has-active-peek ~
- =/ used-pids=(list @ud)
- ~(tap in ~(key by peek-requests.state))
- =/ max-pid=@ud
- (roll used-pids max)
- =/ next-pid=@ud +(max-pid)
- ?: =(next-pid 0) `1 :: handle wraparound
- `next-pid
- --
- :: +plan: core for managing draft relationships
- ::
- :: provides methods for adding, removing, and navigating the draft tree.
- :: uses the axal structure to maintain relationships between drafts, inputs,
- :: and seeds.
- ::
- ++ plan
- |_ tree=draft-tree
- ::
- :: +get-draft: retrieve a draft by name
- ::
- ++ get-draft
- |= name=draft-name
- ^- (unit draft)
- =/ res (~(get of tree) /draft/[name])
- ?~ res ~
- ?. ?=(%draft -.u.res) ~
- `draft.u.res
- :: +get-input: retrieve an input by name
- ::
- ++ get-input
- |= name=input-name
- ^- (unit preinput)
- =/ res (~(get of tree) /input/[name])
- ?~ res ~
- ?. ?=(%input -.u.res) ~
- `preinput.u.res
- :: +get-seed: retrieve a seed by name
- ::
- ++ get-seed
- |= name=seed-name
- ^- (unit preseed)
- =/ res (~(get of tree) /seed/[name])
- ?~ res ~
- ?. ?=(%seed -.u.res) ~
- `preseed.u.res
- :: +add-draft: add a new draft
- ::
- ++ add-draft
- |= [name=draft-name =draft]
- ^- draft-tree
- =/ entity [%draft name draft]
- (~(put of tree) /draft/[name] entity)
- :: +add-input: add a new input
- ::
- ++ add-input
- |= [name=input-name =preinput]
- ^- draft-tree
- =/ entity [%input name preinput]
- (~(put of tree) /input/[name] entity)
- :: +add-seed: add a new seed
- ::
- ++ add-seed
- |= [name=seed-name =preseed]
- ^- draft-tree
- =/ entity [%seed name preseed]
- (~(put of tree) /seed/[name] entity)
- :: +link-input-to-draft: link an input to a draft
- ::
- ++ link-input-to-draft
- |= [draft-name=draft-name input-name=input-name]
- ^- draft-tree
- =/ input-entity (~(get of tree) /input/[input-name])
- ?~ input-entity tree
- ?. ?=(%input -.u.input-entity) tree
- (~(put of tree) /draft/[draft-name]/input/[input-name] u.input-entity)
- :: +link-seed-to-input: link a seed to an input
- ::
- ++ link-seed-to-input
- |= [input-name=input-name seed-name=seed-name]
- ^- draft-tree
- =/ seed-entity (~(get of tree) /seed/[seed-name])
- ?~ seed-entity tree
- ?. ?=(%seed -.u.seed-entity) tree
- (~(put of tree) /input/[input-name]/seed/[seed-name] u.seed-entity)
- :: +unlink-input-from-draft: remove an input from a draft
- ::
- ++ unlink-input-from-draft
- |= [draft-name=draft-name input-name=input-name]
- ^- draft-tree
- (~(del of tree) /draft/[draft-name]/input/[input-name])
- :: +unlink-seed-from-input: remove a seed from an input
- ::
- ++ unlink-seed-from-input
- |= [input-name=input-name seed-name=seed-name]
- ^- draft-tree
- (~(del of tree) /input/[input-name]/seed/[seed-name])
- :: +list-draft-inputs: list all inputs in a draft
- ::
- ++ list-draft-inputs
- |= name=draft-name
- ^- (list input-name)
- =/ kids (~(kid of tree) /draft/[name])
- %+ murn ~(tap in ~(key by kids))
- |= pax=pith
- ^- (unit input-name)
- =/ pax=path (pout pax)
- ?> ?=([%input *] pax)
- ?> ?=(^ t.pax)
- `i.t.pax
- :: +list-input-seeds: list all seeds in an input
- ::
- ++ list-input-seeds
- |= name=input-name
- ^- (list seed-name)
- =/ kids (~(kid of tree) /input/[name])
- %+ murn ~(tap in ~(key by kids))
- |= pax=pith
- ^- (unit seed-name)
- =/ pax=path (pout pax)
- ?: &(?=([%seed *] pax) ?=(^ t.pax))
- `i.t.pax
- ~
- :: +list-all-drafts: list all draft names
- ::
- ++ list-all-drafts
- ^- (list draft-name)
- =/ kids (~(kid of tree) /draft)
- %+ murn ~(tap in ~(key by kids))
- |= pax=pith
- ^- (unit draft-name)
- =/ pax=path (pout pax)
- ?: ?=(^ pax)
- `i.pax
- ~
- :: +list-all-inputs: list all input names
- ::
- ++ list-all-inputs
- ^- (list input-name)
- =/ kids (~(kid of tree) /input)
- %+ murn ~(tap in ~(key by kids))
- |= pax=pith
- ^- (unit input-name)
- =/ pax=path (pout pax)
- ?: ?=(^ pax)
- `i.pax
- ~
- :: +list-all-seeds: list all seed names
- ::
- ++ list-all-seeds
- ^- (list seed-name)
- =/ kids (~(kid of tree) /seed)
- %+ murn ~(tap in ~(key by kids))
- |= pax=pith
- ^- (unit seed-name)
- =/ pax=path (pout pax)
- ?: &(?=([%seed *] pax) ?=(^ t.pax))
- `i.pax
- ~
- :: +remove-draft: completely remove a draft and its associations
- ::
- ++ remove-draft
- |= name=draft-name
- ^- draft-tree
- (~(lop of tree) /draft/[name])
- :: +remove-input: completely remove an input and its associations
- ::
- ++ remove-input
- |= name=input-name
- ^- draft-tree
- (~(lop of tree) /input/[name])
- :: +remove-seed: completely remove a seed and its associations
- ::
- ++ remove-seed
- |= name=seed-name
- ^- draft-tree
- (~(lop of tree) /seed/[name])
- --
- ::
- ++ make-markdown-effect
- |= nodes=markdown:m
- [%markdown (crip (en:md nodes))]
- ::
- ++ display-poke
- |= =cause
- ^- effect
- =/ nodes=markdown:m
- %- need
- %- de:md
- %- crip
- """
- ## poke
- {<cause>}
- """
- (make-markdown-effect nodes)
- ::
- ++ display-note
- |= note=nnote:transact
- ^- markdown:m
- %- need
- %- de:md
- %- crip
- """
- ## details
- - name: {<(to-b58:nname:transact name.note)>}
- - assets: {<assets.note>}
- - source: {<source.note>}
- ## lock
- - m: {<m.lock.note>}
- - signers: {<(to-b58:signers:lock:transact lock.note)>}
- """
- ::
- ++ show
- |= [=state =path]
- ^- [(list effect) ^state]
- |^
- ?+ path !!
- [%balance ~]
- :- ~[(display-balance balance.state)]
- state
- ::
- [%receive-address ~]
- :- (display-receive-address receive-address.state)
- state
- ::
- [%state ~]
- :- display-state
- state
- ==
- ++ display-balance
- |= =balance
- ^- effect
- =/ nodes=markdown:m
- %- need
- %- de:md
- %- crip
- """
- ## balance
- {<balance>}
- """
- (make-markdown-effect nodes)
- ::
- ++ display-receive-address
- |= address=lock:transact
- ^- (list effect)
- =/ nodes=markdown:m
- %- need
- %- de:md
- %- crip
- """
- ## receive address
- {<address>}
- """
- ~[(make-markdown-effect nodes)]
- ::
- ++ display-state
- ^- (list effect)
- =/ nodes=markdown:m
- %- need
- %- de:md
- %- crip
- """
- ## state
- - last block: {<last-block.state>}
- """
- ~[(make-markdown-effect nodes)]
- --
- --
- ::
- %- (moat &)
- ^- fort:moat
- |_ =state
- +* v ~(. vault state)
- d ~(. draw state)
- e ~(. edit state)
- p ~(. plan draft-tree.state)
- ::
- ++ load
- |= arg=^state
- ^- ^state
- arg
- ::
- ++ peek
- |= =path
- ^- (unit (unit *))
- %- (debug "peek: {<state>}")
- ?+ path ~
- ::
- [%balance ~]
- ``balance.state
- ::
- [%receive-address ~]
- ``receive-address.state
- ::
- [%state ~]
- ``state
- ==
- ::
- ++ poke
- |= =ovum:moat
- |^
- ^- [(list effect) ^state]
- %- (warn "debug printing may expose sensitive information")
- =/ =wire wire.ovum
- =+ [@ src=@ta ver=@ rest=*]=wire.ovum
- %- (debug "wire: {<[src ver `path`rest]>}")
- =/ cause=(unit cause)
- %- (soft cause)
- cause.input.ovum
- =/ success [%exit 0]
- =/ failure [%exit 1]
- ?~ cause
- %- (debug "input does not have a proper cause: {<cause.input.ovum>}")
- [~[failure] state]
- =/ cause u.cause
- %- (debug "cause: {<-.cause>}")
- =; process
- =^ effs state process
- :: check for pending balance commands and execute them
- =^ pending-effs state handle-pending-commands
- [(weld effs pending-effs) state]
- ?- -.cause
- %npc-bind (handle-npc cause)
- %show (show state path.cause)
- %keygen (do-keygen cause)
- %derive-child (do-derive-child cause)
- %sign-tx (do-sign-tx cause)
- %scan (do-scan cause)
- %list-notes (do-list-notes cause)
- %list-notes-by-pubkey (do-list-notes-by-pubkey cause)
- %simple-spend (do-simple-spend cause)
- %update-balance (do-update-balance cause)
- %update-block (do-update-block cause)
- %import-keys (do-import-keys cause)
- %import-master-pubkey (do-import-master-pubkey cause)
- %gen-master-privkey (do-gen-master-privkey cause)
- %gen-master-pubkey (do-gen-master-pubkey cause)
- %make-tx (do-make-tx cause)
- %list-pubkeys (do-list-pubkeys cause)
- %sync-run (do-sync-run cause)
- %show-seedphrase (do-show-seedphrase cause)
- %show-master-pubkey (do-show-master-pubkey cause)
- %show-master-privkey (do-show-master-privkey cause)
- ::
- %advanced-spend
- ?- +<.cause
- %seed (do-advanced-spend-seed +>.cause)
- %input (do-advanced-spend-input +>.cause)
- %draft (do-advanced-spend-draft +>.cause)
- ==
- ::
- %file
- ?> ?=(%write +<.cause)
- ~& > "%file %write: {<cause>}"
- [[%exit 0]~ state]
- ==
- ::
- ++ handle-npc
- |= =npc-cause
- ^- [(list effect) ^state]
- %- (debug "handle-npc: {<-.npc-cause>}")
- ?- -.npc-cause
- %npc-bind
- =/ pid pid.npc-cause
- =/ peek-type
- ?. (~(has by peek-requests.state) pid)
- ~|("no peek request found for pid: {<pid>}" !!)
- (~(got by peek-requests.state) pid)
- =/ result result.npc-cause
- ?- peek-type
- %balance
- =/ softed=(unit (unit (unit (z-map:zo nname:transact nnote:transact))))
- %- (soft (unit (unit (z-map:zo nname:transact nnote:transact))))
- result
- =. peek-requests.state
- (~(del by peek-requests.state) pid)
- ?~ softed
- %- (debug "handle-npc: %balance: could not soft result")
- `state
- =/ balance-result=(unit (unit _balance.state)) u.softed
- ?~ balance-result
- %- (warn "%update-balance did not return a result: bad path")
- `state
- ?~ u.balance-result
- %- (warn "%update-balance did not return a result: nothing")
- `state
- ?~ u.u.balance-result
- %- (warn "%update-balance did not return a result: empty result")
- `state
- =. balance.state u.u.balance-result
- %- (debug "balance state updated!")
- =. name-to-hash.state
- %- ~(run z-by:zo balance.state)
- |= not=nnote:transact
- ^- hash:transact
- (hash:nnote:transact not)
- =. hash-to-name.state
- %- ~(gas z-by:zo *(z-map:zo hash:transact nname:transact))
- %+ turn ~(tap z-by:zo name-to-hash.state)
- |= [name=nname:transact =hash:transact]
- [hash name]
- :: move each command from balance phase to ready phase
- =/ balance-commands=(list [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]])
- %+ skim ~(tap z-by:zo pending-commands.state)
- |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
- =(phase %balance)
- =. pending-commands.state
- %- ~(gas z-by:zo pending-commands.state)
- %+ turn balance-commands
- |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
- [pid [%ready wrapped]]
- ::
- :: the top-level poke arm should check for pending commands
- :: and execute them as appropriate
- %- (debug "handle-npc: %balance: balance updated, pending commands ready for execution")
- [~ state]
- ::
- %block
- %- (debug "handle-npc: %block")
- =/ softed=(unit (unit (unit (unit block-id:transact))))
- %- (soft (unit (unit (unit block-id:transact))))
- result
- =. peek-requests.state
- (~(del by peek-requests.state) pid)
- ?~ softed
- %- (warn "handle-npc: %block: could not soft result")
- `state
- =/ block-result u.softed
- %- (debug "handle-npc: %block: {<block-result>}")
- %- (debug "handle-npc: %block: {<peek-requests.state>}")
- ?~ block-result
- %- (warn "handle-npc: %block: bad path")
- `state
- ?~ u.block-result
- %- (warn "handle-npc: %block: nothing")
- `state
- ?~ u.u.block-result
- %- (warn "handle-npc: %block: empty result")
- `state
- %- (debug "handle-npc: %block: found block")
- %- (debug "handle-npc: {<(to-b58:block-id:transact (need u.u.block-result))>}")
- %- (debug "handle-npc: hash: {<(to-b58:hash:transact (need u.u.block-result))>}")
- =. last-block.state u.u.block-result
- :: move each command from block phase to balance phase
- =/ block-commands=(list [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]])
- %+ skim ~(tap z-by:zo pending-commands.state)
- |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
- =(phase %block)
- ::
- %- (debug "handle-npc: %block: preparing {<(lent block-commands)>} commands for balance update")
- :: move each command to balance update phase
- =. pending-commands.state
- %- ~(gas z-by:zo pending-commands.state)
- %+ turn block-commands
- |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
- [pid [%balance wrapped]]
- :: check if we need to update balance (if there are commands waiting for it)
- =/ have-balance-cmds=?
- %- ~(any z-by:zo pending-commands.state)
- |= [phase=?(%block %balance %ready) wrapped=*]
- =(phase %balance)
- ::
- ?: have-balance-cmds
- %- (debug "handle-npc: %block: initiating balance update for pending commands")
- =^ balance-update-effs state
- (do-update-balance [%update-balance ~])
- [balance-update-effs state]
- `state
- ::
- ==
- ==
- ::
- ++ handle-pending-commands
- ^- [(list effect) ^state]
- =/ ready-commands=(list [pid=@ud =cause])
- %+ turn
- %+ skim ~(tap z-by:zo pending-commands.state)
- |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
- =(phase %ready)
- |= [pid=@ud [phase=?(%block %balance %ready) wrapped=cause]]
- [pid wrapped]
- ?~ ready-commands
- %- (debug "no pending commands to execute")
- `state
- %- (debug "executing {<(lent ready-commands)>} pending commands")
- :: process each command
- =/ effs=(list effect) ~
- =/ cmds=(list [pid=@ud =cause]) ready-commands
- |-
- ?~ cmds
- [effs state]
- =/ pid=@ud pid.i.cmds
- =/ =cause +.i.cmds
- =/ ov=^ovum
- %* . ovum
- cause.input cause
- ==
- =. pending-commands.state
- (~(del z-by:zo pending-commands.state) pid)
- ::
- =^ cmd-effs state
- (poke ov)
- $(cmds t.cmds, effs (weld effs cmd-effs))
- ::
- ++ do-sync-run
- |= =cause
- ?> ?=(%sync-run -.cause)
- %- (debug "sync-run: wrapped command {<-.wrapped.cause>}")
- :: get a new pid for the command
- =/ pid=(unit @ud) (generate-pid:v %block)
- ?~ pid
- :: if we can't get a pid, run the command directly
- %- (debug "sync-run: no pid available, running command directly")
- =/ ov=^ovum
- %* . ovum
- cause.input wrapped.cause
- ==
- (poke ov)
- :: store the command in pending-commands
- =. pending-commands.state
- %+ ~(put z-by:zo pending-commands.state)
- u.pid
- [%block wrapped.cause]
- :: initiate block update
- =^ block-update-effs state
- (do-update-block [%update-block ~])
- :: return block update effects
- [block-update-effs state]
- ::
- ++ do-update-balance
- |= =cause
- ?> ?=(%update-balance -.cause)
- %- (debug "update-balance")
- %- (debug "last balance size: {<(lent ~(tap z-by:zo balance.state))>}")
- =/ pid=(unit @ud) (generate-pid:v %balance)
- ?~ pid `state
- =/ bid=block-id:transact
- ?: ?=(~ last-block.state)
- ~|("no last block found, not updating balance" !!)
- (need last-block.state)
- =/ =path (snoc /balance (to-b58:block-id:transact bid))
- =/ =effect [%npc u.pid %peek path]
- :- ~[effect]
- state(peek-requests (~(put by peek-requests.state) u.pid %balance))
- ::
- ++ do-update-block
- |= =cause
- ?> ?=(%update-block -.cause)
- %- (debug "update-block")
- %- (debug "last block: {<last-block.state>}")
- =/ pid=(unit @ud) (generate-pid:v %block)
- ?~ pid `state
- =/ =path /heavy
- =/ =effect [%npc u.pid %peek path]
- :- ~[effect]
- state(peek-requests (~(put by peek-requests.state) u.pid %block))
- ::
- ++ do-import-keys
- |= =cause
- ?> ?=(%import-keys -.cause)
- =/ new-keys=_keys.state
- %+ roll keys.cause
- |= [[=trek =coil] acc=_keys.state]
- (~(put of acc) trek coil)
- `state(keys new-keys)
- ::
- ::>) TODO: update state.master as well
- ++ do-import-master-pubkey
- |= =cause
- ?> ?=(%import-master-pubkey -.cause)
- %- (debug "import-master-pubkey: {<key.cause>}")
- =/ c=coil [%coil [%pub (de:base58:wrap (trip key.cause))] (de:base58:wrap (trip cc.cause))]
- =/ cor (from-public:s10 [p.key.c cc.c])
- =/ master-pubkey-coil=coil [%coil [%pub public-key] chain-code]:cor
- =. master.state (some master-pubkey-coil)
- =/ label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
- %- (debug "Imported master public key: {<public-key:cor>}")
- =. keys.state (key:put:v master-pubkey-coil ~ label)
- `state
- ::
- ++ do-gen-master-privkey
- |= =cause
- ?> ?=(%gen-master-privkey -.cause)
- :: We do not need to reverse the endian-ness of the seedphrase
- :: because the bip39 code expects a tape.
- =/ seed=byts [64 (to-seed:bip39 (trip seedphrase.cause) "")]
- =/ cor (from-seed:s10 seed)
- =/ master-pubkey-coil=coil [%coil [%pub public-key] chain-code]:cor
- =/ master-privkey-coil=coil [%coil [%prv private-key] chain-code]:cor
- =. master.state (some master-pubkey-coil)
- =/ public-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
- =/ private-label `(crip "master-private-{<(end [3 4] public-key:cor)>}")
- =. keys.state (key:put:v master-privkey-coil ~ private-label)
- =. keys.state (key:put:v master-pubkey-coil ~ public-label)
- =. keys.state (seed:put:v seedphrase.cause)
- %- (debug "master.state: {<master.state>}")
- [[%exit 0]~ state]
- ::
- ++ do-gen-master-pubkey
- |= =cause
- ?> ?=(%gen-master-pubkey -.cause)
- =/ cor (from-private:s10 master-privkey.cause)
- =/ master-pubkey-coil=coil [%coil [%pub public-key] chain-code]:cor
- =/ master-privkey-coil=coil [%coil [%prv private-key] chain-code]:cor
- %- (debug "Generated master public key: {<public-key:cor>}")
- =/ public-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
- =/ private-label `(crip "master-private-{<(end [3 4] public-key:cor)>}")
- =. master.state (some master-pubkey-coil)
- =. keys.state (key:put:v master-privkey-coil ~ private-label)
- =. keys.state (key:put:v master-pubkey-coil ~ public-label)
- %- (debug "master.state: {<master.state>}")
- `state
- ::
- ++ do-make-tx
- |= =cause
- ?> ?=(%make-tx -.cause)
- %- (debug "make-tx: {<dat.cause>}")
- :: note that new:raw-tx calls +validate already
- =/ raw=raw-tx:transact (new:raw-tx:transact p.dat.cause)
- =/ tx-id id.raw
- =/ nock-cause=$>(%fact cause:dumb)
- [%fact %0 %heard-tx raw]
- %- (debug "make-tx: new-tx {<tx-id>}")
- :_ state
- :~
- [%npc 0 %poke nock-cause]
- [%exit 0]
- ==
- ::
- ++ do-list-pubkeys
- |= =cause
- ?> ?=(%list-pubkeys -.cause)
- =/ pubkeys ~(coils get:v %pub)
- =/ base58-keys=(list tape)
- %+ turn pubkeys
- |= =coil
- =/ pubkey=schnorr-pubkey:transact pub:(from-public:s10 [p.key cc]:coil)
- %- trip
- (to-b58:schnorr-pubkey:transact pubkey)
- %- (debug "base58-keys: {<base58-keys>}")
- :- [%exit 0]~
- state
- ::
- ++ do-show-seedphrase
- |= =cause
- ?> ?=(%show-seedphrase -.cause)
- %- (debug "show-seedphrase")
- =/ =meta seed:get:v
- =/ seedphrase=@t
- ?: ?=(%seed -.meta)
- +.meta
- %- crip
- "no seedphrase found"
- :_ state
- :~ :- %markdown
- %- crip
- """
- ## seedphrase
- {<seedphrase>}
- """
- [%exit 0]
- ==
- ::
- ++ do-show-master-pubkey
- |= =cause
- ?> ?=(%show-master-pubkey -.cause)
- %- (debug "show-master-pubkey")
- =/ =meta ~(master get:v %pub)
- ?> ?=(%coil -.meta)
- :_ state
- :~ :- %markdown
- %- crip
- """
- ## master public key
- {(en:base58:wrap p.key.meta)}
- """
- [%exit 0]
- ==
- ::
- ++ do-show-master-privkey
- |= =cause
- ?> ?=(%show-master-privkey -.cause)
- %- (debug "show-master-privkey")
- =/ =meta ~(master get:v %prv)
- ?> ?=(%coil -.meta)
- :_ state
- :~ :- %markdown
- %- crip
- """
- ## master private key
- {(en:base58:wrap p.key.meta)}
- """
- [%exit 0]
- ==
- ++ do-scan
- |= =cause
- ?> ?=(%scan -.cause)
- %- (debug "scan: scanning {<search-depth.cause>} addresses")
- ?> ?=(^ master.state)
- :: get all public keys up to search depth
- =/ index=@ud search-depth.cause
- =/ coils=(list coil)
- =/ keys=(list coil) [~(master get:v %pub)]~
- =| done=_|
- |- ^- (list coil)
- ?: done keys
- =? done =(0 index) &
- =/ base=trek /keys/pub/[ux/p.key:(public:master master.state)]/[ud/index]
- =/ key=(unit coil)
- ;; (unit coil)
- (~(get of keys.state) base)
- %= $
- index ?:(=(0 index) 0 (dec index))
- keys ?^(key (snoc keys u.key) keys)
- ==
- :: fail when no coils
- ?: ?=(~ coils)
- ~|("no coils for master key" !!)
- :: generate first names of notes owned by each pubkey
- =/ first-names=(list [hash:transact schnorr-pubkey:transact])
- %+ turn coils
- |= =coil
- :: create lock from public key
- =/ pubkey=schnorr-pubkey:transact pub:(from-public:s10 [p.key cc]:coil)
- =/ =lock:transact (new:lock:transact pubkey)
- :: generate name and take first name
- =/ match-name=nname:transact
- %- new:nname:transact
- :* lock
- [*hash:transact %.n] :: empty source, not a coinbase
- *timelock:transact :: no timelock
- ==
- [-.match-name pubkey]
- :: find notes with matching first names in balance
- =/ notes=(list nnote:transact)
- %+ murn
- ~(tap z-by:zo balance.state)
- |= [name=nname:transact note=nnote:transact]
- ^- (unit nnote:transact)
- :: check if first name matches any in our list
- =/ matches
- %+ lien first-names
- |= [first-name=hash:transact pubkey=schnorr-pubkey:transact]
- =/ =lock:transact (new:lock:transact pubkey)
- :: update lock if include-multisig is true and pubkey is in
- :: the multisig set in the note's lock
- =? lock
- ?& include-multisig.cause
- (~(has z-in:zo pubkeys.lock.note) pubkey)
- ==
- lock.note
- :: update match-name if include-timelocks is set
- =? first-name include-timelocks.cause
- =< -
- %- new:nname:transact
- :* lock
- [*hash:transact %.n] :: empty source, not a coinbase
- timelock.note :: include timelock
- ==
- =(-.name first-name)
- ?:(matches `note ~)
- %- (debug "found matches: {<notes>}")
- =/ nodes=markdown:m
- :~ :- %leaf
- :- %heading
- :* %atx 1
- :~ [%text 'Scan Result']
- ==
- ==
- :- %container
- :- %ul
- :* 0 '*'
- (turn notes display-note)
- ==
- ==
- :_ state
- ~[(make-markdown-effect nodes)]
- ::
- ++ do-list-notes
- |= =cause
- ?> ?=(%list-notes -.cause)
- %- (debug "list-notes")
- =/ notes=(list [name=nname:transact note=nnote:transact])
- %+ sort ~(tap z-by:zo balance.state)
- |= $: a=[name=nname:transact note=nnote:transact]
- b=[name=nname:transact note=nnote:transact]
- ==
- (gth assets.note.a assets.note.b)
- =/ nodes=markdown:m
- %+ welp
- %- need
- %- de:md
- %- crip
- """
- ## wallet notes
- """
- %- zing
- %+ turn notes
- |= [* =nnote:transact]
- (display-note nnote)
- :_ state
- ~[(make-markdown-effect nodes) [%exit 0]]
- ::
- ++ do-list-notes-by-pubkey
- |= =cause
- ~& "list-notes-by-pubkey"
- ?> ?=(%list-notes-by-pubkey -.cause)
- ~& "list-notes-by-pubkey: {<pubkey.cause>}"
- =/ target-pubkey=schnorr-pubkey:transact
- (from-b58:schnorr-pubkey:transact pubkey.cause)
- =/ matching-notes=(list [name=nname:transact note=nnote:transact])
- %+ skim ~(tap z-by:zo balance.state)
- |= [name=nname:transact note=nnote:transact]
- (~(has z-in:zo pubkeys.lock.note) target-pubkey)
- =/ sorted-notes=(list [name=nname:transact note=nnote:transact])
- %+ sort matching-notes
- |= $: a=[name=nname:transact note=nnote:transact]
- b=[name=nname:transact note=nnote:transact]
- ==
- (gth assets.note.a assets.note.b)
- =/ nodes=markdown:m
- %+ welp
- %- need
- %- de:md
- %- crip
- """
- ## wallet notes for pubkey {<(to-b58:schnorr-pubkey:transact target-pubkey)>}
- """
- %- zing
- %+ turn sorted-notes
- |= [* =nnote:transact]
- (display-note nnote)
- :_ state
- ~[(make-markdown-effect nodes) [%raw sorted-notes] [%exit 0]]
- ::
- ++ do-simple-spend
- |= =cause
- ?> ?=(%simple-spend -.cause)
- %- (debug "simple-spend: {<names.cause>}")
- :: for now, each input corresponds to a single name and recipient. all
- :: assets associated with the name are transferred to the recipient.
- ::
- :: thus there is one recipient per name, and one seed per recipient.
- ::
- =/ names=(list nname:transact)
- %+ turn names.cause
- |= [first=@t last=@t]
- (from-b58:nname:transact [first last])
- =/ recipients=(list lock:transact)
- %+ turn recipients.cause
- |= [m=@ pks=(list @t)]
- %+ m-of-n:new:lock:transact m
- %- ~(gas z-in:zo *(z-set:zo schnorr-pubkey:transact))
- %+ turn pks
- |= pk=@t
- (from-b58:schnorr-pubkey:transact pk)
- ::
- =/ gifts=(list coins:transact) gifts.cause
- ::
- ?. ?& =((lent names) (lent recipients))
- =((lent names) (lent gifts))
- ==
- ~|("different number of names/recipients/gifts" !!)
- =| ledger=(list [name=nname:transact recipient=lock:transact gifts=coins:transact])
- =. ledger
- |-
- ?~ names ledger
- :: since we assert they are equal in length above, this is just to get
- :: the i face
- ?~ gifts ledger
- ?~ recipients ledger
- %= $
- ledger [[i.names i.recipients i.gifts] ledger]
- names t.names
- gifts t.gifts
- recipients t.recipients
- ==
- ::
- :: the fee is subtracted from the first note that permits doing so without overspending
- =/ fee=coins:transact fee.cause
- :: use the first private key to construct the subsequent inputs
- =/ sender=coil
- =/ private-keys=(list coil) ~(coils get:v %prv)
- ?~ private-keys
- ~|("No private keys available for signing" !!)
- (head private-keys)
- =/ sender-key=schnorr-seckey:transact
- (from-atom:schnorr-seckey:transact p.key.sender)
- :: for each name, create an input from the corresponding note in sender's
- :: balance at the current block. the fee will be subtracted entirely from
- :: the first note that has sufficient assets for both the fee and the gift.
- :: the refund is sent to receive-address.state
- =/ [ins=(list input:transact) spent-fee=?]
- %^ spin ledger `?`%.n
- |= $: $: name=nname:transact
- recipient=lock:transact
- gift=coins:transact
- ==
- spent-fee=?
- ==
- =/ note=nnote:transact (get-note:v name)
- ?: (gth gift assets.note)
- ~| "gift {<gift>} larger than assets {<assets.note>} for recipient {<recipient>}"
- !!
- ?: ?& !spent-fee
- (lte (add gift fee) assets.note)
- ==
- :: we can subtract the fee from this note
- :_ %.y
- %- with-choice:with-refund:simple-from-note:new:input:transact
- [recipient gift fee note sender-key receive-address.state]
- :: :: we cannot subtract the fee from this note, or we already have from a previous one
- :_ %.n
- %- with-choice:with-refund:simple-from-note:new:input:transact
- [recipient gift 0 note sender-key receive-address.state]
- ::
- ?. spent-fee
- ~|("no note suitable to subtract fee from, aborting operation" !!)
- =/ ins-draft=inputs:transact (multi:new:inputs:transact ins)
- ?: ?=(~ last-block.state)
- ~|("last-block unknown!" !!)
- :: name is the b58-encoded name of the first input
- =/ draft-name=@t
- %- head
- %+ turn ~(tap z-by:zo (names:inputs:transact ins-draft))
- |= =nname:transact
- =< last
- (to-b58:nname:transact nname)
- :: jam inputs and save as draft
- =/ =draft
- %* . *draft
- p ins-draft
- name draft-name
- ==
- =/ draft-jam (jam draft)
- =/ path=@t
- %- crip
- "./drafts/{(trip name.draft)}.draft"
- %- (debug "saving draft to {<path>}")
- =/ =effect [%file %write path draft-jam]
- :- ~[effect [%exit 0]]
- state
- ::
- ++ do-keygen
- |= =cause
- ?> ?=(%keygen -.cause)
- =+ [seed-phrase=@t cor]=(gen-master-key:s10 entropy.cause salt.cause)
- =/ master-public-coil [%coil [%pub public-key] chain-code]:cor
- =/ master-private-coil [%coil [%prv private-key] chain-code]:cor
- =. master.state (some master-public-coil)
- %- (debug "keygen: new public key {<public-key:cor>}")
- %- (debug "keygen: public key: base58 {<(en:base58:wrap public-key:cor)>}")
- %- (debug "keygen: new private key {<private-key:cor>}")
- %- (debug "keygen: private key: base58 {<(en:base58:wrap private-key:cor)>}")
- =/ pub-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
- =/ prv-label `(crip "master-public-{<(end [3 4] public-key:cor)>}")
- =. keys.state (key:put:v master-public-coil ~ pub-label)
- =. keys.state (key:put:v master-private-coil ~ prv-label)
- =. keys.state (seed:put:v seed-phrase)
- :_ state
- :~ :- %markdown
- %- crip
- """
- ## Keygen
- ### New Public Key
- {<(en:base58:wrap public-key:cor)>}
- ### New Private Key
- {<(en:base58:wrap private-key:cor)>}
- """
- [%exit 0]
- ==
- ::
- :: derives child %pub or %prv key of current master key
- :: at index `i`. this will overwrite existing paths.
- ++ do-derive-child
- |= =cause
- ?> ?=(%derive-child -.cause)
- =/ par=coil ~(master get:v key-type.cause)
- =/ child=coil (derive-child:v par i.cause)
- =. keys.state
- (key:put:v child `i.cause label.cause)
- :- [%exit 0]~
- state
- ::
- ++ do-sign-tx
- |= =cause
- ?> ?=(%sign-tx -.cause)
- %- (debug "sign-tx: {<dat.cause>}")
- :: get private key at specified index, or first derived key if no index
- =/ private-keys=(list coil) ~(coils get:v %prv)
- ?~ private-keys
- ~|("No private keys available for signing" !!)
- =/ sender=coil
- ?~ index.cause i.private-keys
- =/ key-at-index=meta (~(by-index get:v %prv) u.index.cause)
- ?> ?=(%coil -.key-at-index)
- key-at-index
- =/ sender-key=schnorr-seckey:transact
- (from-atom:schnorr-seckey:transact p.key.sender)
- =/ signed-inputs=inputs:transact
- %- ~(run z-by:zo p.dat.cause)
- |= =input:transact
- %- (debug "signing input: {<input>}")
- =. spend.input
- %+ sign:spend:transact
- spend.input
- sender-key
- input
- =/ signed-draft=draft
- %= dat.cause
- p signed-inputs
- ==
- =/ draft-jam (jam signed-draft)
- =/ path=@t
- %- crip
- "./drafts/{(trip name.signed-draft)}.draft"
- %- (debug "saving input draft to {<path>}")
- =/ =effect [%file %write path draft-jam]
- :- ~[effect [%exit 0]]
- state
- ::
- ++ do-advanced-spend-seed
- |= cause=advanced-spend-seed
- ^- [(list effect) ^state]
- |^
- =? active-draft.state ?=(~ active-draft.state) `*draft-name
- =? active-seed.state ?=(~ active-seed.state) `*seed-name
- ?- -.cause
- %new do-new
- %set-name do-set-name
- %set-source do-set-source
- %set-recipient do-set-recipient
- %set-timelock do-set-timelock
- %set-gift do-set-gift
- %set-parent-hash do-set-parent-hash
- %set-parent-hash-from-name do-set-parent-hash-from-name
- %print-status do-print-status
- ==
- ::
- ++ do-new
- ?> ?=([%new *] cause)
- =/ sed=preseed
- %* . *preseed
- name name.cause
- ==
- (write-seed sed)
- ::
- ++ do-set-name
- ?> ?=([%set-name *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =. name.u.pre new-name.cause
- (write-seed u.pre)
- ::
- ++ do-set-source
- ?> ?=([%set-source *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =/ sed=preseed
- ?~ source.cause
- %= u.pre
- output-source.p ~
- output-source.q %.y
- ==
- %= u.pre
- output-source.p (some (from-b58:source:transact u.source.cause))
- output-source.q %.y
- ==
- (write-seed sed)
- ::
- ++ do-set-recipient
- ?> ?=([%set-recipient *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =/ recipient=lock:transact
- %+ m-of-n:new:lock:transact m.recipient.cause
- %- ~(gas z-in:zo *(z-set:zo schnorr-pubkey:transact))
- (turn pks.recipient.cause from-b58:schnorr-pubkey:transact)
- =/ sed=preseed
- %= u.pre
- recipient.p recipient
- recipient.q %.y
- ==
- (write-seed sed)
- ::
- ++ do-set-timelock
- ?> ?=([%set-timelock *] cause)
- ::TODO
- !!
- ::
- ++ do-set-gift
- ?> ?=([%set-gift *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =/ sed=preseed
- %= u.pre
- gift.p gift.cause
- gift.q %.y
- ==
- (write-seed sed)
- ::
- ++ do-set-parent-hash
- ?> ?=([%set-parent-hash *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =/ sed=preseed
- %= u.pre
- parent-hash.q %.y
- parent-hash.p (from-b58:hash:transact parent-hash.cause)
- ==
- (write-seed sed)
- ::
- ++ do-set-parent-hash-from-name
- ?> ?=([%set-parent-hash-from-name *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =/ name=nname:transact (from-b58:nname:transact name.cause)
- =/ not=nnote:transact (get-note:v name)
- =/ sed=preseed
- %= u.pre
- parent-hash.p (hash:nnote:transact not)
- parent-hash.q %.y
- ==
- (write-seed sed)
- ::
- ++ do-print-status
- ?> ?=([%print-status *] cause)
- =/ pre=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ pre)
- =/ output-source-text=tape
- ?: !output-source.q.u.pre
- "Unset (any output source is OK)"
- <output-source.p.u.pre>
- =/ recipient-text=tape
- ?: !recipient.q.u.pre
- "Unset"
- <recipient.p.u.pre>
- =/ timelock-text=tape
- ?: !timelock-intent.q.u.pre
- "Unset (no intent)"
- <timelock-intent.p.u.pre>
- =/ gift-text=tape
- ?: !gift.q.u.pre
- "Gift: unset (gift must be nonzero)"
- ?: =(0 gift.p.u.pre)
- """
- Gift: 0 (must be nonzero)
- """
- """
- Gift: {<gift.p.u.pre>}
- """
- =/ status-text=tape
- """
- ## Seed Status
- ### Output Source
- {output-source-text}
- ### Recipient
- {recipient-text}
- ### Timelock Intent
- {timelock-text}
- ### Gift
- {gift-text}
- """
- :_ state
- (print (need (de:md (crip status-text))))
- ::
- ++ write-seed
- |= sed=preseed
- ^- [(list effect) ^state]
- =. active-seed.state (some name.sed)
- =. draft-tree.state (~(add-seed plan draft-tree.state) name.sed sed)
- =^ writes state write-draft:d
- [writes state]
- -- ::+do-advanced-spend-seed
- ::
- ++ do-advanced-spend-input
- |= cause=advanced-spend-input
- ^- [(list effect) ^state]
- |^
- ?- -.cause
- %new do-new
- %set-name do-set-name
- %add-seed do-add-seed
- %set-fee do-set-fee
- %set-note-from-name do-set-note-from-name
- %set-note-from-hash do-set-note-from-hash
- %derive-note-from-seeds do-derive-note-from-seeds
- %remove-seed do-remove-seed
- %remove-seed-by-hash do-remove-seed-by-hash
- %print-status do-print-status
- ==
- ::
- ++ do-new
- ?> ?=([%new *] cause)
- =/ inp=preinput
- %* . *preinput
- name name.cause
- ==
- =. active-input.state (some name.cause)
- (write-input inp)
- ::
- ++ do-set-name
- ?> ?=([%set-name *] cause)
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =. name.u.pre new-name.cause
- =. active-input.state (some new-name.cause)
- (write-input u.pre)
- ::
- ++ do-add-seed
- ?> ?=([%add-seed *] cause)
- ::
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =/ sed=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ sed)
- ?: (~(has z-in:zo seeds.spend.p.u.pre) p.u.sed)
- :_ state
- =/ nodes=markdown:m
- :~ :- %leaf
- :- %paragraph
- :~ [%text (crip "seed already exists in .spend, doing nothing.")]
- ==
- ==
- (print nodes)
- =/ inp=preinput
- %= u.pre
- seeds.spend.p (~(put z-in:zo seeds.spend.p.u.pre) p.u.sed)
- seeds.spend.q %.y
- ==
- (write-input inp)
- ::
- ++ do-set-fee
- ?> ?=([%set-fee *] cause)
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =. fee.spend.p.u.pre fee.cause
- =. fee.spend.q.u.pre %.y
- (write-input u.pre)
- ::
- ++ do-set-note-from-name
- ?> ?=([%set-note-from-name *] cause)
- ::
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =/ name=nname:transact (from-b58:nname:transact name.cause)
- =/ not=nnote:transact (get-note:v name)
- =/ inp=preinput
- %= u.pre
- note.p not
- note.q %.y
- ==
- (write-input inp)
- ::
- ++ do-set-note-from-hash
- ?> ?=([%set-note-from-hash *] cause)
- ::
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =/ =hash:transact (from-b58:hash:transact hash.cause)
- =/ note=nnote:transact (get-note-from-hash:v hash)
- =/ inp=preinput
- %= u.pre
- note.p note
- note.q %.y
- ==
- (write-input inp)
- ::
- ++ do-derive-note-from-seeds
- ?> ?=([%derive-note-from-seeds *] cause)
- ::
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =/ seeds-list=(list seed:transact)
- ~(tap z-in:zo seeds.spend.p.u.pre)
- ?~ seeds-list
- :_ state
- =/ nodes=markdown:m
- :~ :- %leaf
- :- %paragraph
- :~ [%text (crip "no seeds exist in .spend, so note cannot be set.")]
- ==
- ==
- (print nodes)
- =/ =hash:transact parent-hash.i.seeds-list
- =/ note=nnote:transact (get-note-from-hash:v hash)
- =/ inp=preinput
- %= u.pre
- note.p note
- note.q %.y
- ==
- (write-input inp)
- ::
- ++ do-remove-seed
- ?> ?=([%remove-seed *] cause)
- ::
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =. active-input.state (some input-name.cause)
- =/ sed=(unit preseed)
- (get-seed:p seed-name.cause)
- ?> ?=(^ sed)
- ?: !(~(has z-in:zo seeds.spend.p.u.pre) p.u.sed)
- :_ state
- =/ nodes=markdown:m
- :~ :- %leaf
- :- %paragraph
- :~ [%text (crip "seed does not exist in .spend, doing nothing")]
- ==
- ==
- (print nodes)
- =/ inp=preinput
- %= u.pre
- seeds.spend.p (~(del z-in:zo seeds.spend.p.u.pre) p.u.sed)
- seeds.spend.q !=(*seeds:transact seeds.spend.p.u.pre)
- ==
- (write-input inp)
- ::
- ++ do-remove-seed-by-hash
- ?> ?=([%remove-seed-by-hash *] cause)
- :: find seed with hash
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =. active-input.state (some input-name.cause)
- =/ seed-hashes=(z-map:zo hash:transact seed:transact)
- %- ~(gas z-by:zo *(z-map:zo hash:transact seed:transact))
- %+ turn ~(tap z-in:zo seeds.spend.p.u.pre)
- |= sed=seed:transact
- [(hash:seed:transact sed) sed]
- =/ has=hash:transact (from-b58:hash:transact hash.cause)
- ?. (~(has z-by:zo seed-hashes) has)
- :_ state
- =/ nodes=markdown:m
- :~ :- %leaf
- :- %paragraph
- :~ [%text (crip "seed does not exist in .spend, doing nothing")]
- ==
- ==
- (print nodes)
- =/ remove-seed=seed:transact
- (~(got z-by:zo seed-hashes) has)
- ::
- =/ inp=preinput
- %= u.pre
- seeds.spend.p (~(del z-in:zo seeds.spend.p.u.pre) remove-seed)
- seeds.spend.q !=(*seeds:transact seeds.spend.p.u.pre)
- ==
- (write-input inp)
- ::
- ++ do-print-status
- ?> ?=([%print-status *] cause)
- =/ pre=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ pre)
- =| status-nodes=markdown:m
- =. status-nodes
- %+ snoc status-nodes
- :- %leaf
- :- %paragraph
- ?: !signature.spend.q.u.pre
- :: TODO we removed the ability to sign in this control flow
- [%text (crip ".signature: unset")]~
- :: check the signature
- ::
- :: get a .parent-hash of a seed. they have to all be the same, so which
- :: one doesn't matter; if they're not all the same validation will fail.
- =/ seeds-list=(list seed:transact)
- ~(tap z-in:zo seeds.spend.p.u.pre)
- ?~ seeds-list
- [%text (crip "no seeds exist, so signature cannot be checked")]~
- =/ parent-note-hash=hash:transact parent-hash.i.seeds-list
- =/ parent-note-hash-b58=tape
- (trip (to-b58:hash:transact parent-note-hash))
- =/ parent-note-name=(unit nname:transact)
- (~(get z-by:zo hash-to-name.state) parent-note-hash)
- ?~ parent-note-name
- :~
- :- %text
- %- crip
- """
- note with hash {parent-note-hash-b58} present in .spend but
- has no matching .name in wallet
- """
- :- %text
- :: TODO better, more succint error message.
- '''
- this implies that it is not in the balance unless there is a hash collision.
- please report this as a bug if you are sure you have the $note, as this
- situation is very unlkely. the spend ought to still be valid in that case
- and you can broadcast it anyway.
- '''
- ==
- =/ parent-note-name-b58=tape
- =; [first=@t last=@t]
- "<(trip first)> <(trip last)>"
- (to-b58:nname:transact u.parent-note-name)
- =/ parent-note=(unit nnote:transact)
- (~(get z-by:zo balance.state) u.parent-note-name)
- ?~ parent-note
- :~
- :- %text
- %- crip
- """
- note with name {parent-note-name-b58} and hash {parent-note-hash-b58}
- present in .spend but not in balance
- """
- ==
- ?: (verify:spend:transact spend.p.u.pre u.parent-note)
- [%text (crip "signature(s) on spend are valid.")]~
- :: missing or invalid sigs
- =/ have-sigs
- %+ turn
- %~ tap z-in:zo
- ^- (z-set:zo schnorr-pubkey:transact)
- %~ key z-by:zo (need signature.spend.p.u.pre)
- |= pk=schnorr-pubkey:transact
- [%text (to-b58:schnorr-pubkey:transact pk)]
- ?~ have-sigs
- [%text 'no signatures found!']~
- =/ lock-b58=[m=@ pks=(list @t)]
- (to-b58:lock:transact recipient.i.seeds-list)
- =/ need-sigs
- (turn pks.lock-b58 (lead %text))
- ?~ need-sigs
- [%text 'no recipients found!']~
- ;: welp
- :~ [%text (crip "signature on spend did not validate.")]
- [%text (crip "signatures on spend:")]
- ==
- ::TODO check if any particular signature did not validate
- have-sigs
- :~ [%text (crip ".lock on parent note:")]
- [%text (crip "number of sigs required: {(scow %ud m.lock-b58)}")]
- [%text (crip "pubkeys of possible signers:")]
- ==
- need-sigs
- ==
- ::TODO check individual seeds? this would require some refactoring and
- ::the happy path does not involve adding unfinished seeds to an input.
- :_ state
- (print status-nodes)
- ::
- ++ write-input
- |= inp=preinput
- ^- [(list effect) ^state]
- =. active-input.state (some name.inp)
- =. draft-tree.state (~(add-input plan draft-tree.state) name.inp inp)
- =^ writes state write-draft:d
- [writes state]
- -- ::+do-advanced-spend-input
- ::
- ++ do-advanced-spend-draft
- |= cause=advanced-spend-draft
- ^- [(list effect) ^state]
- |^
- =? active-draft.state ?=(~ active-draft.state) `*draft-name
- ?- -.cause
- %new do-new
- %set-name do-set-name
- %add-input do-add-input
- %remove-input do-remove-input
- %remove-input-by-name do-remove-input-by-name
- %print-status do-print-status
- ==
- ::
- ++ do-new
- ?> ?=([%new *] cause)
- =. active-draft.state (some name.cause)
- =/ dat=draft
- %* . *draft
- name name.cause
- ==
- (write-draft dat)
- ::
- ++ do-set-name
- ?> ?=([%set-name *] cause)
- =/ pre=(unit draft)
- (get-draft:p draft-name.cause)
- ?> ?=(^ pre)
- =. active-draft.state (some new-name.cause)
- =. name.u.pre new-name.cause
- (write-draft u.pre)
- ::
- ++ do-add-input
- ?> ?=([%add-input *] cause)
- =/ pre=(unit draft)
- (get-draft:p draft-name.cause)
- ?> ?=(^ pre)
- =. active-draft.state (some draft-name.cause)
- =/ inp=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ inp)
- ?: (~(has z-by:zo p.u.pre) name.note.p.u.inp)
- :_ state
- %- print
- ^- markdown:m
- :_ ~ :- %leaf
- :- %paragraph
- :_ ~ :- %text
- %- crip
- """
- draft already has input with note name
- {<(to-b58:nname:transact name.note.p.u.inp)>}, doing nothing.
- """
- =. p.u.pre
- (~(put z-by:zo p.u.pre) [name.note.p.u.inp p.u.inp])
- (write-draft u.pre)
- ::
- ++ do-remove-input
- ?> ?=([%remove-input *] cause)
- =/ pre=(unit draft)
- (get-draft:p draft-name.cause)
- ?> ?=(^ pre)
- =. active-draft.state (some draft-name.cause)
- =/ inp=(unit preinput)
- (get-input:p input-name.cause)
- ?> ?=(^ inp)
- ?. (~(has z-by:zo p.u.pre) name.note.p.u.inp)
- :_ state
- %- print
- :_ ~ :- %leaf
- :- %paragraph
- :_ ~ :- %text
- %- crip
- """
- draft does not have input with note name
- {<(to-b58:nname:transact name.note.p.u.inp)>}, doing nothing.
- """
- ?. =(u.inp (~(got z-by:zo p.u.pre) name.note.p.u.inp))
- :_ state
- %- print
- :_ ~ :- %leaf
- :- %paragraph
- :_ ~ :- %text
- %- crip
- """
- draft has input with note name
- {<(to-b58:nname:transact name.note.p.u.inp)>}, but it is
- a different input. to remove this input, use %remove-input-by-name
- instead.
- """
- =. p.u.pre
- (~(del z-by:zo p.u.pre) name.note.p.u.inp)
- (write-draft u.pre)
- ::
- ++ do-remove-input-by-name
- ?> ?=([%remove-input-by-name *] cause)
- =/ pre=(unit draft)
- (get-draft:p draft-name.cause)
- =. active-draft.state (some draft-name.cause)
- ?> ?=(^ pre)
- =/ name=nname:transact (from-b58:nname:transact name.cause)
- ?. (~(has z-by:zo p.u.pre) name)
- :_ state
- %- print
- :_ ~ :- %leaf
- :- %paragraph
- :_ ~ :- %text
- %- crip
- """
- draft does not have input with note name {(trip first.name.cause)}
- {(trip last.name.cause)}, doing nothing.
- """
- =. p.u.pre (~(del z-by:zo p.u.pre) name)
- (write-draft u.pre)
- ::
- ++ do-print-status
- ?> ?=([%print-status *] cause)
- =/ pre=(unit draft)
- (get-draft:p draft-name.cause)
- =. active-draft.state (some draft-name.cause)
- ?> ?=(^ pre)
- =/ inputs=(list [name=nname:transact =input:transact])
- ~(tap z-by:zo p.u.pre)
- =/ input-texts=(list tape)
- %+ turn inputs
- |= [name=nname:transact =input:transact]
- =/ signature-text=tape ?~(signature.spend.input "unset" "set")
- =/ name-text=tape <(to-b58:nname:transact name)>
- =/ note-text=tape <(to-b58:hash:transact (hash:nnote:transact note.input))>
- =/ seeds-text=tape
- %- zing
- %+ turn ~(tap z-in:zo seeds.spend.input)
- |= =seed:transact
- """
- - recipient: {<(to-b58:lock:transact recipient.seed)>}
- - gift: {<gift.seed>}
- - parent hash: {<(to-b58:hash:transact parent-hash.seed)>}
- """
- """
- #### Input {name-text}:
- - Note hash: {note-text}
- - Fee: {<fee.spend.input>}
- - Signature: {signature-text}
- ##### Seeds
- {seeds-text}
- """
- =/ status-text=tape
- """
- ## Draft Status
- Name: {(trip name.u.pre)}
- Number of inputs: {<(lent inputs)>}
- ### Inputs
- {(zing input-texts)}
- """
- :_ state
- (print (need (de:md (crip status-text))))
- ::
- ++ write-draft
- |= dat=draft
- ^- [(list effect) ^state]
- =. active-draft.state (some name.dat)
- write-draft:d
- -- ::+do-advanced-spend-draft
- -- ::+poke
- --
|