         (swap! lru-cache cache/miss k new-entry)
         new-entry))))
 
-;;;
-;;; Client internals
-;;;
 (defonce client-lru-cache
   ;; FIXME: magic number
   (atom (cache/lru-cache-factory {} :threshold 64)))
 
-(defn make-client [msp-id name priv-key cert {:keys [roles account affiliation]}]
-  (let [new-client (hf-client/create-new-instance)]
-    (hf-client/set-crypto-suite new-client (crypto-suite/get-crypto-suite))
-    (hf-client/set-user-context new-client 
-                             (reify User
+(defn make-hf-client [msp-id name priv-key cert {:keys [roles account affiliation]}]
+  (let [client (HFClient/createNewInstance)]
+    (.setCryptoSuite client (CryptoSuite$Factory/getCryptoSuite))
+    (.setUserContext client (reify User
                                (getName [this] name)
-                               ;; Set<String>
+                               ;; FIXME: Set<String>
                                (getRoles [this] roles)
                                (getAccount [this] account)
                                (getAffiliation [this] affiliation)

    (get-order-transaction-result tx-future #(println "OK" %) #(println "ERROR" %)))
  
  )


#_
(comment
  ;; Copy from fabcar example
  (defonce options
    {:channel-id "mychannel" :chaincode-id "fabcar" :network-url "grpc://localhost:7051"
     :peer-admin
     {:name "PeerAdmin"
      :mspid "Org1MSP" :roles nil :affiliation "" 
      :enrollment-secret "" 
      :enrollment {:signing-identity "cd96d5260ad4757551ed4a5a991e62130f8008a0bf996e4e4b84cd097a747fec" 
                   :identity {:certificate "-----BEGIN CERTIFICATE-----\nMIICGDCCAb+gAwIBAgIQFSxnLAGsu04zrFkAEwzn6zAKBggqhkjOPQQDAjBzMQsw\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy\nYW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eu\nb3JnMS5leGFtcGxlLmNvbTAeFw0xNzA4MzEwOTE0MzJaFw0yNzA4MjkwOTE0MzJa\nMFsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T\nYW4gRnJhbmNpc2NvMR8wHQYDVQQDDBZBZG1pbkBvcmcxLmV4YW1wbGUuY29tMFkw\nEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV1dfmKxsFKWo7o6DNBIaIVebCCPAM9C/\nsLBt4pJRre9pWE987DjXZoZ3glc4+DoPMtTmBRqbPVwYcUvpbYY8p6NNMEswDgYD\nVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwKwYDVR0jBCQwIoAgQjmqDc122u64\nugzacBhR0UUE0xqtGy3d26xqVzZeSXwwCgYIKoZIzj0EAwIDRwAwRAIgXMy26AEU\n/GUMPfCMs/nQjQME1ZxBHAYZtKEuRR361JsCIEg9BOZdIoioRivJC+ZUzvJUnkXu\no2HkWiuxLsibGxtE\n-----END CERTIFICATE-----\n"}}}})

  ;; 1. Client setup
  (defonce cli (hf-client/create-new-instance))
  (hf-client/set-crypto-suite cli (crypto-suite/get-crypto-suite))
 
  ;; 2. User context
  (defonce peer-admin-enrollment
    (reify Enrollment
      ;; FIXME: enrollment!
      ;; How to iniitiate key and cert?
      (getKey [this]
        (-> (slurp (str "resources/creds/"
                     (->  options
                          (get-in [:peer-admin :enrollment :signing-identity])
                          (str "-priv"))))
            (keys/str->private-key)))
      (getCert [this]
        (get-in options [:peer-admin :enrollment :identity :certificate]))))
  
  (defonce peer-admin
    (reify User
      (getName [this]
        (get-in options [:peer-admin :name]))
      ;; Set<String>
      (getRoles [this]
        (get-in options [:peer-admin :roles]))
      (getAccount [this]
        nil)
      (getAffiliation [this]
        (get-in options [:peer-admin :affiliation]))
      (getEnrollment [this]
        peer-admin-enrollment)
      (getMspId [this]
        (get-in options [:peer-admin :mspid]))))
  
  (hf-client/set-user-context cli peer-admin)

  
  ;; 2. Channel
  (defonce chan (hf-client/new-channel cli (:channel-id options)))
  (channel/add-orderer chan (hf-client/new-orderer cli "orderer0" "grpc://localhost:7050"))
  (channel/add-peer chan (hf-client/new-peer cli "peer0" "grpc://localhost:7051"))
  (channel/initialize chan)

  ;; 3. Request
  (defonce req (hf-client/new-transaction-proposal-request cli))

  (defonce chaincode-id (-> (chaincode-id/new-builder)
                            (chaincode-id/set-name "fabcar")
                            (chaincode-id/set-version "1.0")
                            (chaincode-id/set-path "resources/")
                            (chaincode-id/build)))
  (transaction-request/set-chaincode-id req chaincode-id)
  (transaction-request/set-fcn req "createCar")
  ;; timeout
  (transaction-request/set-proposal-wait-time req 10000)
  ;; FIXME: marshall
  (transaction-request/set-args req (java.util.ArrayList. ["CAR10" "Chevy" "Volt" "Red" "Nick"]))

  (defonce resp (channel/send-transaction-proposal chan req))
  (defonce future1 (channel/send-transaction chan resp (hf-client/get-user-context cli)))
  (.get future1)
  )


;;;
;;; The basic idea and initial code are from Amazonica
;;; - https://github.com/mcohen01/amazonica
;;;
(ns clojure-fabric.core
  "Clojure wrapper for Hyperledger Java SDK functions"
  (:use [clojure.algo.generic.functor :only (fmap)])
  (:require [clojure.string :as str]
            [clojure.core.cache :as cache]
            [buddy.core.keys :as keys])
  (:import clojure.lang.Reflector
           [org.hyperledger.fabric.sdk HFClient User Enrollment]
           [org.hyperledger.fabric.sdk.security CryptoSuite$Factory]
           [org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey]
           [java.text SimpleDateFormat ParsePosition]
           java.util.Date
           org.joda.time.DateTime
           org.joda.time.base.AbstractInstant
           java.io.File           
           java.nio.ByteBuffer
           [java.lang.reflect InvocationTargetException ParameterizedType
            Method Modifier]

           ;; java.io.PrintWriter
           ;; java.io.StringWriter
           ;; java.math.BigDecimal
           ;; java.math.BigInteger



           )
  ;; (:require [clojure.algo.generic.functor :as algo]
  ;;           [clojure.string :as str]
  ;;           [buddy.core.certificates :as certs]
  ;;           [buddy.core.keys :as keys]
  ;;           [promissum.core :as promis])
  
  ;; (:import [org.hyperledger.fabric.sdk User Enrollment Peer Orderer]
  ;;          [org.bouncycastle.jcajce.provider.asymmetric.ec BCECPrivateKey])
  )

;;;
;;; Dynamic variables
;;; 
(def ^:dynamic ^:private *hf-client* nil)
(def ^:dynamic ^:private *channel* nil)
(def ^:dynamic ^:private *peers* nil)
(def ^:dynamic ^:private *orderers* nil)
(def ^:dynamic ^:private *proposal-request* nil)
(def ^:dynamic ^:private *proposal-response* nil)
(def ^:dynamic ^:private *order-request* nil)
(def ^:dynamic ^:private *order-response* nil)

;;;
;;;
;;;
(def ^:private root-unwrapping (atom false))

(defn set-root-unwrapping!
  "Enables JSON-like root unwrapping of singly keyed
  top level maps.
    {:root {:key 'foo' :name 'bar'}}
  would become
    {:key 'foo' :name 'bar'}"
  [b]
  (reset! root-unwrapping b))



(def ^:private date-format (atom "yyyy-MM-dd"))

(defn set-date-format!
  "Sets the java.text.SimpleDateFormat pattern to use
  for transparent coercion of Strings passed as
  arguments where java.util.Dates are required by the API."
  [df]
  (reset! date-format df))


;;;
;;; Client - manually construct
;;;
(defn- general-lru-cache-read [k lru-cache miss-hit-fn & fn-args]
    (let [cache-hit (swap! lru-cache cache/hit k)]
      (if-let [found (cache/lookup cache-hit k)]
