; The DID innerpuzzle is designed to sit inside the singleton layer and provide functionality related to being an identity. ; At the moment the two pieces of functionality are recovery and message creation. ; A DID's ID is it's Singleton ID ; Recovery is based around having a list of known other DIDs which can send messages approving you change the innerpuzzle of your DID singleton (mod ( INNER_PUZZLE ; Standard P2 inner puzzle, used to record the ownership of the DID. RECOVERY_DID_LIST_HASH ; the list of DIDs that can send messages to you for recovery we store only the hash so that we don't have to reveal every time we make a message spend NUM_VERIFICATIONS_REQUIRED ; how many of the above list are required for a recovery SINGLETON_STRUCT ; my singleton_struct, formerly a Truth - ((SINGLETON_MOD_HASH, (LAUNCHER_ID, LAUNCHER_PUZZLE_HASH))) METADATA ; Customized metadata, e.g KYC info mode ; this indicates which spend mode we want. 0. Recovery mode 1. Run INNER_PUZZLE with p2_solution my_amount_or_inner_solution ; In mode 0, we use this to recover our coin and assert it is our actual amount ; In mode 1 this is the solution of the inner P2 puzzle, only required in the create message mode and transfer mode. new_inner_puzhash ; In recovery mode, this will be the new wallet DID puzzle hash parent_innerpuzhash_amounts_for_recovery_ids ; during a recovery we need extra information about our recovery list coins pubkey ; this is the new pubkey used for a recovery recovery_list_reveal ; this is the reveal of the stored list of DIDs approved for recovery my_id ; my coin ID ) ;message is the new puzzle in the recovery and standard spend cases ;MOD_HASH, MY_PUBKEY, RECOVERY_DID_LIST_HASH are curried into the puzzle ;EXAMPLE SOLUTION (0xcafef00d 0x12341234 0x923bf9a7856b19d335a65f12d68957d497e1f0c16c0e14baf6d120e60753a1ce 2 1 100 (q "source code") 0xdeadbeef 0xcafef00d ((0xdadadada 0xdad5dad5 200) () (0xfafafafa 0xfaf5faf5 200)) 0xfadeddab (0x22222222 0x33333333 0x44444444)) (include condition_codes.klvm) (include curry-and-treehash.clinc) ; takes a lisp tree and returns the hash of it (defun sha256tree1 (TREE) (if (l TREE) (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) (sha256 1 TREE) ) ) ; recovery message module - gets values curried in to make the puzzle (defun make_message_puzzle (recovering_coin newpuz pubkey) (qq (q . (((unquote CREATE_COIN_ANNOUNCEMENT) (unquote recovering_coin)) ((unquote AGG_SIG_UNSAFE) (unquote pubkey) (unquote newpuz))))) ) ; this function creates the assert announcement for each message coin approving a recovery (defun-inline create_consume_message (coin_id my_id new_innerpuz pubkey) (list ASSERT_COIN_ANNOUNCEMENT (sha256 (sha256 coin_id (sha256tree1 (make_message_puzzle my_id new_innerpuz pubkey))) my_id)) ) ; this function calculates a coin ID given the inner puzzle and singleton information (defun create_coin_ID_for_recovery (SINGLETON_STRUCT launcher_id parent innerpuzhash amount) (sha256 parent (calculate_full_puzzle_hash (c (f SINGLETON_STRUCT) (c launcher_id (r (r SINGLETON_STRUCT)))) innerpuzhash) amount) ) ; return the full puzzlehash for a singleton with the innerpuzzle curried in ; puzzle-hash-of-curried-function is imported from curry-and-treehash.clib (defun-inline calculate_full_puzzle_hash (SINGLETON_STRUCT inner_puzzle_hash) (puzzle-hash-of-curried-function (f SINGLETON_STRUCT) inner_puzzle_hash (sha256tree1 SINGLETON_STRUCT) ) ) ; this loops over our identities to check list, and checks if we have been given parent information for this identity ; the reason for this is because we might only require 3/5 of the IDs give approval messages for a recovery ; if we have the information for an identity then we create a consume message using that information (defun check_messages_from_identities (SINGLETON_STRUCT num_verifications_required identities my_id new_puz parent_innerpuzhash_amounts_for_recovery_ids pubkey num_verifications) (if identities (if (f parent_innerpuzhash_amounts_for_recovery_ids) ; if we have parent information then we should create a consume coin condition (c (create_consume_message ; create coin_id from DID (create_coin_ID_for_recovery SINGLETON_STRUCT (f identities) (f (f parent_innerpuzhash_amounts_for_recovery_ids)) (f (r (f parent_innerpuzhash_amounts_for_recovery_ids))) (f (r (r (f parent_innerpuzhash_amounts_for_recovery_ids))))) my_id new_puz pubkey ) (check_messages_from_identities SINGLETON_STRUCT num_verifications_required (r identities) my_id new_puz (r parent_innerpuzhash_amounts_for_recovery_ids) pubkey (+ num_verifications 1) ) ) ; if no parent information found for this identity, move on to next in list (check_messages_from_identities SINGLETON_STRUCT (r identities) my_id new_puz (r parent_innerpuzhash_amounts_for_recovery_ids) pubkey num_verifications ) ) ;if we're out of identites to check for, check we have enough (if (> num_verifications (- num_verifications_required 1)) (list (list AGG_SIG_UNSAFE pubkey new_puz) ) (x) ) ) ) ;Spend modes: ;0 = recovery ;1 = run the INNER_PUZZLE ;MAIN (if mode ; mode 1 - run INNER_PUZZLE (a INNER_PUZZLE my_amount_or_inner_solution) ; mode 0 - recovery (if (all (= (sha256tree1 recovery_list_reveal) RECOVERY_DID_LIST_HASH) (> NUM_VERIFICATIONS_REQUIRED 0)) (c (list ASSERT_MY_AMOUNT my_amount_or_inner_solution) (c (list CREATE_COIN new_inner_puzhash my_amount_or_inner_solution (list new_inner_puzhash)) (c (list ASSERT_MY_COIN_ID my_id) (check_messages_from_identities SINGLETON_STRUCT NUM_VERIFICATIONS_REQUIRED recovery_list_reveal my_id new_inner_puzhash parent_innerpuzhash_amounts_for_recovery_ids pubkey 0) ) ) ) (x) ) ) )