; build a pay-to delegated puzzle or hidden puzzle
; coins can be unlocked by signing a delegated puzzle and its solution
; OR by revealing the hidden puzzle and the underlying original key

; glossary of parameter names:

; hidden_puzzle: a "hidden puzzle" that can be revealed and used as an alternate
;   way to unlock the underlying funds
;
; synthetic_key_offset: a private key cryptographically generated using the hidden
;   puzzle and as inputs `original_public_key`
;
; SYNTHETIC_PUBLIC_KEY: the public key that is the sum of `original_public_key` and the
;   public key corresponding to `synthetic_key_offset`
;
; original_public_key: a public key, where knowledge of the corresponding private key
;   represents ownership of the file
;
; delegated_puzzle: a delegated puzzle, as in "graftroot", which should return the
;   desired conditions.
;
; solution: the solution to the delegated puzzle


(mod
    ; A puzzle should commit to `SYNTHETIC_PUBLIC_KEY`
    ;
    ; The solution should pass in 0 for `original_public_key` if it wants to use
    ; an arbitrary `delegated_puzzle` (and `solution`) signed by the
    ; `SYNTHETIC_PUBLIC_KEY` (whose corresponding private key can be calculated
    ; if you know the private key for `original_public_key`)
    ;
    ; Or you can solve the hidden puzzle by revealing the `original_public_key`,
    ; the hidden puzzle in `delegated_puzzle`, and a solution to the hidden puzzle.

    (SYNTHETIC_PUBLIC_KEY original_public_key delegated_puzzle solution)

    ; "assert" is a macro that wraps repeated instances of "if"
    ; usage: (assert A0 A1 ... An R)
    ; all of A0, A1, ... An must evaluate to non-null, or an exception is raised
    ; return the value of R (if we get that far)

    (defmacro assert items
        (if (r items)
            (list if (f items) (c assert (r items)) (q . (x)))
            (f items)
        )
    )

    (include condition_codes.clib)

    ;; hash a tree
    ;; This is used to calculate a puzzle hash given a puzzle program.
    (defun sha256tree1
           (TREE)
           (if (l TREE)
               (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
               (sha256 1 TREE)
           )
    )

    ; "is_hidden_puzzle_correct" returns true iff the hidden puzzle is correctly encoded

    (defun-inline is_hidden_puzzle_correct (SYNTHETIC_PUBLIC_KEY original_public_key delegated_puzzle)
      (=
          SYNTHETIC_PUBLIC_KEY
          (point_add
              original_public_key
              (pubkey_for_exp (sha256 original_public_key (sha256tree1 delegated_puzzle)))
          )
      )
    )

    ; "possibly_prepend_aggsig" is the main entry point

    (defun-inline possibly_prepend_aggsig (SYNTHETIC_PUBLIC_KEY original_public_key delegated_puzzle conditions)
      (if original_public_key
          (assert
              (is_hidden_puzzle_correct SYNTHETIC_PUBLIC_KEY original_public_key delegated_puzzle)
              conditions
          )
          (c (list AGG_SIG_ME SYNTHETIC_PUBLIC_KEY (sha256tree1 delegated_puzzle)) conditions)
      )
    )

    ; main entry point

    (possibly_prepend_aggsig
        SYNTHETIC_PUBLIC_KEY original_public_key delegated_puzzle
        (a delegated_puzzle solution))
)
