├── .gitignore ├── .java-version ├── lib └── secp256k1-1.0.3.jar ├── config ├── prod │ └── config.edn └── dev │ └── config.edn ├── src └── prsc │ ├── assetmapping.clj │ ├── crypto.clj │ ├── utils.clj │ ├── eos.clj │ ├── mixin.clj │ ├── error.clj │ ├── verify.clj │ ├── dsl.clj │ └── core.clj ├── CHANGELOG.md ├── Dockerfile ├── examples └── example.prsc ├── test └── prsc │ ├── verify_test.clj │ └── core_test.clj ├── LICENSE ├── project.clj └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /doc 3 | -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | openjdk64-11.0.2 2 | -------------------------------------------------------------------------------- /lib/secp256k1-1.0.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Press-One/prsc/HEAD/lib/secp256k1-1.0.3.jar -------------------------------------------------------------------------------- /config/prod/config.edn: -------------------------------------------------------------------------------- 1 | {:apiroot "https://press.one/api/v2" 2 | :p1s "https://static.press.one/" 3 | :privkey "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 4 | } 5 | -------------------------------------------------------------------------------- /src/prsc/assetmapping.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.assetmapping 2 | "asset mapping" 3 | ) 4 | 5 | (def mixin.one {:PRS "3edb734c-6d6f-32ff-ab03-4eb43640c758" :CNB "965e5c6e-434c-3fa9-b780-c50f43cd955c"}) 6 | -------------------------------------------------------------------------------- /config/dev/config.edn: -------------------------------------------------------------------------------- 1 | {:apiroot "https://dev.press.one/api/v2" 2 | :p1s "https://storage.googleapis.com/pressone/" 3 | :privkey "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 4 | } 5 | -------------------------------------------------------------------------------- /src/prsc/crypto.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.crypto 2 | "crypto functions" 3 | (:require [config.core :refer [env]] 4 | [secp256k1.core :as secp256k1])) 5 | 6 | (defn signature [signtext] 7 | (-> (str (:privkey env)) (secp256k1/private-key :hex) (secp256k1/sign signtext))) 8 | 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [0.1.1] - 2019-05-17 6 | ### Added 7 | - signature of the DSL execute result 8 | 9 | ### Changed 10 | - Upgrade to PRS API v2 11 | - Upgrade to JDK 11 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/prsc/utils.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.utils 2 | "utils functions" 3 | ) 4 | 5 | (defn safenth [v idx] 6 | (if (and (seqable? v) (integer? idx) (< idx (count v)) ) 7 | (nth v idx) 8 | nil 9 | )) 10 | 11 | (defn call [fnname & args] 12 | (apply (resolve (symbol fnname )) args)) 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM clojure 2 | RUN mkdir -p /usr/src/app 3 | WORKDIR /usr/src/app 4 | COPY project.clj /usr/src/app/ 5 | RUN lein deps 6 | COPY . /usr/src/app 7 | EXPOSE 3001 8 | RUN mv "$(lein ring uberjar | sed -n 's/^Created \(.*standalone\.jar\)/\1/p')" app-standalone.jar 9 | CMD ["java", "-jar", "app-standalone.jar"] 10 | -------------------------------------------------------------------------------- /examples/example.prsc: -------------------------------------------------------------------------------- 1 | PRSC Ver 0.1 2 | Name 合约名称 3 | Desc 这是\n测试合约 4 | Receiver becd34540fefeab83730ffb479e98ee12fa1337e 5 | Use 1b48260cb790d4a6fad321333cd3625585f602fa58d2541bceb25eb480fab3da :license-type Personal :arg1 arg1 6 | Use 1b48260cb790d4a6fad321333cd3625585f602fa58d2541bceb25eb480fab3dc :license-type Commercial :arg1 arg1 7 | License Useage01 PRS:0.0013 Terms: 这是个人使用条款,禁止\n商业应用。 8 | License Commercial PRS:0.0035 Terms: 这是商业使用条款,允许\n修改和复制。 9 | -------------------------------------------------------------------------------- /test/prsc/verify_test.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.verify-test 2 | (:require [clojure.test :refer :all] 3 | [clara.rules :refer :all] 4 | [prsc.dsl :refer :all] 5 | [prsc.eos :refer :all] 6 | [prsc.error :refer :all] 7 | [prsc.verify :refer :all] 8 | [prsc.utils :refer :all] 9 | )) 10 | (deftest verify-test 11 | (let [tx (prsc.eos/get-eos-tx 1472676 "0503EA4A4061A7142992AFCFD04DB083E563C5C103E9989E3FBF7B6E89D3F01B")] 12 | (is (= "0503ea4a4061a7142992afcfd04db083e563c5c103e9989e3fbf7b6e89d3f01b" (:id (:trx (prsc.utils/safenth (:tx tx) 0))))) 13 | (let [data (prsc.eos/verifytrx tx)] 14 | (is (= (:type data) "RECEIPT:2")) 15 | (let [prsdata (prsc.verify/verify-PRSdata data)] 16 | (is (= "cd5e8aa1-87df-408d-a5de-28c22e4f2b4c" (:payment_snapshot_id (:data prsdata)))) 17 | (let [result (prsc.verify/parse-PRSData prsdata)] 18 | (is (= true (:result result))) 19 | ) 20 | ) 21 | ) 22 | ) 23 | ) 24 | -------------------------------------------------------------------------------- /test/prsc/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.core-test 2 | (:require [clojure.test :refer :all] 3 | [clara.rules :refer :all] 4 | [prsc.dsl :refer :all] 5 | [prsc.utils :refer :all] 6 | )) 7 | (defn loadprsc [] 8 | (slurp "./doc/example.prsc")) 9 | 10 | (deftest get-license-test 11 | (let [contractcode (loadprsc)] 12 | (let [session (-> (mk-session 'prsc.dsl (load-user-rules contractcode)) 13 | (insert (->license-type "Commercial")) 14 | (fire-rules))] 15 | (let [result (query session get-license)] 16 | (testing "Contract Receiver:" 17 | (is (= (get-in (first result) [:?receiver :address]) "becd34540fefeab83730ffb479e98ee12fa1337e"))) 18 | (testing "Contract Type" 19 | (is (= (get-in (first result) [:?license :type]) "Commercial"))) 20 | (testing "Contract Currency" 21 | (is (= (get-in (first result) [:?license :currency]) "PRS"))) 22 | (testing "Contract price" 23 | (is (= (get-in (first result) [:?license :price]) "0.0035")))) 24 | 25 | ))) 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018 PRESS.one 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject prsc "0.1.0-SNAPSHOT" 2 | :description "PRS light contract interpreter" 3 | :url "https://press.one" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :main prsc.core 7 | :aot [prsc.core] 8 | :profiles {:uberjar {:aot :all :resource-paths ["config/prod/log4j.properties" "lib/secp256k1-1.0.3.jar"]} 9 | :dev {:resource-paths ["config/dev" "config/dev" "lib/secp256k1-1.0.3.jar"]} 10 | :prod {:resource-paths ["config/prod" "lib/secp256k1-1.0.3.jar"]} } 11 | :ring {:handler prsc.core/app :port 3001} 12 | :plugins [ [lein-ring "0.12.5"] [lein-cljfmt "0.6.0"] [lein-cloverage "1.1.2"]] 13 | :repositories [] 14 | :managed-dependencies [[org.clojure/core.rrb-vector "0.0.13"] 15 | [org.flatland/ordered "1.5.7"]] 16 | :dependencies [ 17 | [org.clojure/clojure "1.9.0"], 18 | [org.clojure/data.json "0.2.6"], 19 | [log4j/log4j "1.2.17" :exclusions [javax.mail/mail 20 | javax.jms/jms 21 | com.sun.jdmk/jmxtools 22 | com.sun.jmx/jmxri]], 23 | [metosin/compojure-api "1.1.11"], 24 | [http-kit "2.4.0-alpha5"] 25 | [instaparse "1.4.9"], 26 | [com.cerner/clara-rules "0.18.0"] 27 | [org.apache.commons/commons-compress "1.18"] 28 | [javax.xml.bind/jaxb-api "2.3.0"] 29 | [org.bouncycastle/bcprov-jdk15on "1.61"] 30 | [cheshire "5.8.1"] 31 | [yogthos/config "1.1.1"] 32 | [buddy/buddy-core "1.4.0"] 33 | [buddy/buddy-sign "2.2.0"] 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /src/prsc/eos.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.eos 2 | "eos chain functions" 3 | (:require [config.core :refer [env]] 4 | [cheshire.core :refer :all] 5 | [org.httpkit.client :as http] 6 | [prsc.error :refer :all] 7 | [prsc.utils :refer :all] 8 | ) 9 | (:import [java.net URI] 10 | [javax.net.ssl SNIHostName SNIServerName SSLEngine SSLParameters]) 11 | ) 12 | 13 | (defn sni-configure 14 | [^SSLEngine ssl-engine ^URI uri] 15 | (let [^SSLParameters ssl-params (.getSSLParameters ssl-engine)] 16 | (.setServerNames ssl-params [(SNIHostName. (.getHost uri))]) 17 | (.setUseClientMode ssl-engine true) 18 | (.setSSLParameters ssl-engine ssl-params))) 19 | 20 | 21 | (def sni-client (http/make-client {:timeout 1000 :headers { "Content-Type" "application/json" } :ssl-configurer sni-configure})) 22 | 23 | (defn trxbyid [v trx_id] 24 | (filter (fn [x] (if (= (clojure.string/lower-case trx_id) (clojure.string/lower-case (:id (:trx x)))) x)) v) 25 | ) 26 | 27 | (defn verifytrx [trx] 28 | ;verify sign 29 | (let [trx (:trx (prsc.utils/safenth (:tx trx) 0))] 30 | (if (nil? trx) 31 | nil 32 | (:data (prsc.utils/safenth (:actions (:transaction trx)) 0)) 33 | ) 34 | ) 35 | ) 36 | 37 | (defn get-eos-tx [block_num trx_id] 38 | (let [resp (http/get (str (or (:chainapi env) "https://prs-bp4.press.one/api/chain/blocks/") block_num) {:client sni-client})] 39 | (if (= 200 (:status @resp)) 40 | {:status (:status @resp) :tx (trxbyid (:transactions (:data (parse-string (:body @resp) true))) trx_id) } 41 | (make-error nil PRS_CHAIN_SERVICE_ERROR {:status (:status @resp) :block_num block_num}) 42 | ) 43 | ) 44 | ) 45 | 46 | (defn fetchContractCodeWithrID [contract_id] 47 | (let [resp (http/get (str (:apiroot env) "/blocks/" contract_id))] 48 | (let [block (parse-string (:body @resp) true)] 49 | (if (nil? (:data (first block))) 50 | (make-error nil PRS_NOCONTRACT_ERROR {:rID contract_id}) 51 | (:code (parse-string (:data (first block)) true)) 52 | ) 53 | ))) 54 | -------------------------------------------------------------------------------- /src/prsc/mixin.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.mixin 2 | "mixin functions" 3 | (:require [config.core :refer [env]] 4 | [cheshire.core :refer :all] 5 | [clojure.tools.logging :as log] 6 | [org.httpkit.client :as http] 7 | [buddy.sign.jwt :as jwt] 8 | [buddy.core.keys :as keys] 9 | [buddy.core.hash :as hash] 10 | [buddy.core.codecs :refer :all] 11 | [prsc.error :refer :all] 12 | )) 13 | 14 | (defn jwtToken [method uri body opts] 15 | (let [transfer_sig_str (str method uri body)] 16 | (let [transfer_sig_sha256 (-> (hash/sha256 transfer_sig_str) (bytes->hex)) 17 | seconds (quot (.getTime (new java.util.Date)) 1000) 18 | timeout (or (:time-out opts) 3600) 19 | ] 20 | (let [seconds_exp (+ seconds timeout)] 21 | (let [privatekey (keys/private-key (:privatekey opts))] 22 | (jwt/sign {:uid (:client_id opts) :sid (:session_id opts) :iat seconds :exp seconds_exp :jti (java.util.UUID/randomUUID) :sig transfer_sig_sha256} privatekey {:alg :rs512}) 23 | ) 24 | ) 25 | )) 26 | ) 27 | 28 | (defn getMixinTx [uri viewtoken] 29 | (let [resp (http/get (str (or (:mixinapiroot env) "https://api.mixin.one") uri) {:timeout 1000 30 | :headers {"Authorization" (str "Bearer " viewtoken) 31 | "Content-Type" "application/json" 32 | }})] 33 | (if (= 200 (:status @resp)) 34 | {:status (:status @resp) :data (:data (parse-string (:body @resp) true))} 35 | {:status (:status @resp) :data {}} 36 | ) 37 | ) 38 | ) 39 | 40 | (defn verifyMixinTx [snapshot_id viewtoken trace_id] 41 | (let [uri (str "/network/snapshots/" snapshot_id)] 42 | (let [res (getMixinTx uri viewtoken)] 43 | (log/debug (str "verifyMixinTx : " uri trace_id)) 44 | (log/debug viewtoken) 45 | (if (not= 200 (:status res)) 46 | (make-error nil MIXIN_SERVICE_ERROR {:status (:status res) :uri uri}) 47 | (if (= (:trace_id (:data res)) trace_id) 48 | {:result true} 49 | (make-error false TRACE_ID_NOT_MATCH_ERROR {:l_name "PRS" :l_trace_id trace_id :r_name "MIXIN" :r_trace_id (:trace_id (:data res)) :r_chain_id snapshot_id}) 50 | ) 51 | ) 52 | ) 53 | ) 54 | ) 55 | -------------------------------------------------------------------------------- /src/prsc/error.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.error 2 | "error const define" 3 | ) 4 | 5 | (def ^:const PRS_CHAIN_SERVICE_ERROR 8001) 6 | (def ^:const SIGN_ERROR 8002) 7 | (def ^:const MIXIN_SERVICE_ERROR 8003) 8 | (def ^:const UNSUPPORTED_PAYMENT_SERVICE_ERROR 8500) 9 | (def ^:const PRS_DATA_ERROR 9001) 10 | (def ^:const PRS_DATA_IS_NULL 9002) 11 | (def ^:const BILL_DATA_ERROR 9003) 12 | (def ^:const CONTRACT_DATA_ERROR 9004) 13 | (def ^:const PRS_NOCONTRACT_ERROR 9005) 14 | (def ^:const PRS_RUNCONTRACT_ERROR 9006) 15 | (def ^:const TRACE_ID_NOT_MATCH_ERROR 9007) 16 | (def ^:const RECEIPT_BENEFICIARY_ADDRESS_WALLET_NOT_MATCH_ERROR 9008) 17 | (def ^:const RECEIPT_CONTRACT_PAYMENT_NOT_MATCH_ERROR 9009) 18 | 19 | (defn provider-uid-name [provider] 20 | (cond 21 | (= "MIXIN" provider) "snapshot_id" 22 | :else "unique_id" 23 | ) 24 | ) 25 | 26 | (defn format-error-msg 27 | "error message formatting" 28 | [errcode opt] 29 | (cond 30 | (= errcode TRACE_ID_NOT_MATCH_ERROR) (str "trace_id not match: " (:l_name opt) "_trace_id:" (:l_trace_id opt) " " (:r_name opt) "_trace_id:" (:r_trace_id opt) " at " (:r_name opt) " " (provider-uid-name (:r_name opt)) ":" (:r_chain_id opt)) 31 | (= errcode RECEIPT_BENEFICIARY_ADDRESS_WALLET_NOT_MATCH_ERROR) (str "receipt and beneficiary not match: beneficiary_address " (:beneficiary_address opt) " receipt_beneficiary_address " (:receipt_beneficiary_address opt) " beneficiary_wallet_id " (:beneficiary_wallet_id opt) " receipt_beneficiary_wallet_id "(:receipt_beneficiary_wallet_id opt)) 32 | (= errcode RECEIPT_CONTRACT_PAYMENT_NOT_MATCH_ERROR) (str "receipt and contract not match: contract_address" (:contract_address opt) " receipt_beneficiary_address " (:receipt_beneficiary_address opt) " contract_price " (:contract_price opt) " receipt_amount " (:receipt_amount opt)) 33 | (= errcode MIXIN_SERVICE_ERROR) (str "MIXIN network status code: " (:status opt) " URI: " (:uri opt)) 34 | (= errcode PRS_CHAIN_SERVICE_ERROR) (str "PRS chain network status code:" (:status opt) " block_num: " (:block_num opt)) 35 | (= errcode UNSUPPORTED_PAYMENT_SERVICE_ERROR) (str "provider name: " (:provider opt)) 36 | (= errcode BILL_DATA_ERROR) (str "PRS bill_data block error with bill_id: " (:bill_id opt)) 37 | (= errcode CONTRACT_DATA_ERROR) (str "PRS contract_data block error with contract_id: " (:contract_id opt)) 38 | (= errcode PRS_NOCONTRACT_ERROR) (cond 39 | (some? (:block_num opt)) (str "PRS contract data not found at block_num: " (:block_num opt) " trx_id: " (:trx_id opt)) 40 | (some? (:rID opt)) (str "PRS contract data not found at rID: " (:rID opt)) :else "unexpected error") 41 | (= errcode PRS_DATA_IS_NULL) (str "PRS DATA in the transaction node not found.") 42 | (= errcode PRS_DATA_ERROR) (str "PRS_DATA_ERROR") 43 | :else (str "unexpected error: " errcode)) 44 | ) 45 | 46 | 47 | (defn make-error 48 | "make the error object for API output" 49 | ([result errcode opt] 50 | {:result result :err errcode :msg (format-error-msg errcode opt)}) 51 | ([result errcode] 52 | {:result result :err errcode :msg (format-error-msg errcode nil)}) 53 | ) 54 | 55 | (defn prserr? [obj] 56 | (if (nil? (:err obj)) 57 | false 58 | true 59 | ) 60 | ) 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # prsc 2 | 3 | PRESS.one DSL script interpreter 4 | 5 | ## Development Environment 6 | 7 | 1. install JDK 11 8 | 2. install Clojure 1.9.0 9 | 3. install leiningen https://github.com/technomancy/leiningen 10 | 11 | ## Testing 12 | 13 | lein test 14 | 15 | ## Usage 16 | 17 | Generate a secp256k1 private key, replace the :privkey in the config.edn 18 | 19 | Start the service 20 | 21 | $ env apiroot=https://press.one/api/v2 lein ring server 22 | 23 | curl http://localhost:3001/parser?address=[:address] 24 | 25 | curl http://localhost:3001/license/[:contractrid]?licensetype=Commercial 26 | 27 | 28 | Example: 29 | 30 | ``` 31 | curl http://localhost:3001/v2/parser?address=6ed18d21690433f0410a20bdf5e829975e0da840b48c3cb8e962b17138f5288e 32 | curl http://localhost:3001/v2/license/6ed18d21690433f0410a20bdf5e829975e0da840b48c3cb8e962b17138f5288e?licensetype=usage1 33 | ``` 34 | test contract rid : 35 | ``` 36 | curl https://press.one/api/v2/blocks/6ed18d21690433f0410a20bdf5e829975e0da840b48c3cb8e962b17138f5288e 37 | ``` 38 | 39 | ``` 40 | [ 41 | { 42 | "id": "6ed18d21690433f0410a20bdf5e829975e0da840b48c3cb8e962b17138f5288e", 43 | "user_address": "becd34540fefeab83730ffb479e98ee12fa1337e", 44 | "type": "PUBLISH:1", 45 | "meta": "{\"mime\":\"text/prsc;charset=UTF-8\"}", 46 | "data": "{\"file_hash\":\"42cc05b72e9850151f5dd6cd86cdc0dd02b615272ce387cc6f92f41031e07a22\",\"code\":\"PRSC Ver 0.1\\nName 文章的授权购买\\nDesc 你购买的授权许可不具备排他性,不可再授权,不可转让。\\\\n不得直接转售、分销、共享、转让、出租这些数字内容,不得提供下载,不可嵌入服务器,不可包含在网页模板或设计模板中。\\\\n不得以明示或暗示的方式虚假声明你购买的该数字内容是由你或其它人所创建,或虚假声明你或其他人对你购买的该数字内容持有著作权。\\\\n不可将所购买的数字内容用于任何非法、淫秽、诽谤、虚假内容之中,或用于会被认为是非法、淫秽、诽谤、虚假内容的方式。\\nReceiver becd34540fefeab83730ffb479e98ee12fa1337e\\nLicense usage1 PRS:1 Terms: 个人授权许可,只能用于个人目的的转载、 引用、结集。\\\\n你可以全文或部分转载你购买的文本内容,或在不改变作者原意和立场的情况下编辑文本。任何情况下必须标明原文作者及出处。不标记原作者或出处通常会被视为剽窃行为。\\\\n不可用于商业用途(包括且不限于政府、 教育、 商业机构、非营利组织、社会团体、经营类网站及其它以营利为目的的组织机构或服务)。\\nLicense usage2 PRS:5 Terms: 商用授权许可,用于营利、 商业或经营目的转载、 引用、结集。如:\\\\n- 用于网站、软件、移动应用、社交媒体、邮件、电子书\\\\n- 用于印刷媒体如 杂志、 报纸、 书籍\\\\n- 用于广告、市场活动、宣传推广用途的内容\\\\n你可以全文或部分转载你购买的文本内容,或在不改变作者原意和立场的情况下编辑文本。任何情况下必须标明原文作者及出处。不标记原作者或出处通常会被视为剽窃行为。\"}", 47 | "hash": "038a2f1a0873b85caf08a028f06c57fde8072e0080dd4ae646ea4b721e6563d8", 48 | "signature": "69fa8109a42f23242865574d976c2778d69a021f164aa8841a433bc0837ed39f0ab9c209b25f6be065ac6de4c929bc1bacaba0f0877e5ac277b90f3c312c90e10", 49 | "blockNum": 4072600, 50 | "blockTransactionId": "d426558468e52126d1528ed2661a6d5411c550b19498417604180d32d9ba2ae6" 51 | } 52 | ] 53 | 54 | ``` 55 | 56 | ``` 57 | curl https://press.one/api/v2/contracts/6ed18d21690433f0410a20bdf5e829975e0da840b48c3cb8e962b17138f5288e 58 | ``` 59 | 60 | ``` 61 | { 62 | "file": { 63 | "id": 10945, 64 | "rId": "ebef207952349646817c237d240b57aab005e295190e2c6232b7816e294c5c08", 65 | "address": "becd34540fefeab83730ffb479e98ee12fa1337e", 66 | "msghash": "67d4a2c6b7810aa0b4399b38bc85dd5704b45640b2952fd03a8f65445a35e786", 67 | "title": "穿越寒冬的独行者", 68 | "description": "2018年有很多故事可以写,但是到最后一天的时候,让我选一件事来写,我想写的是duckduckgo这个搜索引擎。选择它是有原因的,这个搜索引擎创始于2008年,正好是第10个年头。 即使在今天,听说过这个搜索引擎的人也不多。上个月(2018.11),它的每日搜索量第一次超过了3000万次,很多科技媒体用非常小的版面报道过这件事。中文也有报道,基本上就是“一句话新闻”这样的待遇,没人多想什么。这不意外,每天3000万搜索听起来不小,但是放在整个搜索市场可以算的上微不足道。做为对比,Google早就不再...", 69 | "originUrl": "", 70 | "cacheUrl": "https://static.press.one/67/d4/67d4a2c6b7810aa0b4399b38bc85dd5704b45640b2952fd03a8f65445a35e786.md", 71 | "mimeType": "text/markdown", 72 | "createdAt": "2018-12-31T07:19:40.000Z", 73 | "source": "", 74 | "projectId": null, 75 | "rewardAmount": "184.529000000000000000000000000000", 76 | "rewardCount": 47 77 | }, 78 | "contract": { 79 | "createdAt": "2018-12-31T07:20:04.000Z", 80 | "code": "PRSC Ver 0.1\nName 文章的授权购买\nDesc 你购买的授权许可不具备排他性,不可再授权,不可转让。\\n不得直接转售、分销、共享、转让、出租这些数字内容,不得提供下载,不可嵌入服务器,不可包含在网页模板或设计模板中。\\n不得以明示或暗示的方式虚假声明你购买的该数字内容是由你或其它人所创建,或虚假声明你或其他人对你购买的该数字内容持有著作权。\\n不可将所购买的数字内容用于任何非法、淫秽、诽谤、虚假内容之中,或用于会被认为是非法、淫秽、诽谤、虚假内容的方式。\nReceiver becd34540fefeab83730ffb479e98ee12fa1337e\nLicense usage1 PRS:1 Terms: 个人授权许可,只能用于个人目的的转载、 引用、结集。\\n你可以全文或部分转载你购买的文本内容,或在不改变作者原意和立场的情况下编辑文本。任何情况下必须标明原文作者及出处。不标记原作者或出处通常会被视为剽窃行为。\\n不可用于商业用途(包括且不限于政府、 教育、 商业机构、非营利组织、社会团体、经营类网站及其它以营利为目的的组织机构或服务)。\nLicense usage2 PRS:5 Terms: 商用授权许可,用于营利、 商业或经营目的转载、 引用、结集。如:\\n- 用于网站、软件、移动应用、社交媒体、邮件、电子书\\n- 用于印刷媒体如 杂志、 报纸、 书籍\\n- 用于广告、市场活动、宣传推广用途的内容\\n你可以全文或部分转载你购买的文本内容,或在不改变作者原意和立场的情况下编辑文本。任何情况下必须标明原文作者及出处。不标记原作者或出处通常会被视为剽窃行为。", 81 | "receiverName": "霍炬", 82 | "receiverAvatar": "https://static.press.one/ee/19/ee19a6825c9c90f6405840d40c63d900.png", 83 | "version": "0.1", 84 | "name": "文章的授权购买", 85 | "description": "你购买的授权许可不具备排他性,不可再授权,不可转让。\\n不得直接转售、分销、共享、转让、出租这些数字内容,不得提供下载,不可嵌入服务器,不可包含在网页模板或设计模板中。\\n不得以明示或暗示的方式虚假声明你购买的该数字内容是由你或其它人所创建,或虚假声明你或其他人对你购买的该数字内容持有著作权。\\n不可将所购买的数字内容用于任何非法、淫秽、诽谤、虚假内容之中,或用于会被认为是非法、淫秽、诽谤、虚假内容的方式。", 86 | "licenses": [ 87 | { 88 | "type": "usage1", 89 | "currency": "PRS", 90 | "price": "1", 91 | "termtext": "个人授权许可,只能用于个人目的的转载、 引用、结集。\\n你可以全文或部分转载你购买的文本内容,或在不改变作者原意和立场的情况下编辑文本。任何情况下必须标明原文作者及出处。不标记原作者或出处通常会被视为剽窃行为。\\n不可用于商业用途(包括且不限于政府、 教育、 商业机构、非营利组织、社会团体、经营类网站及其它以营利为目的的组织机构或服务)。" 92 | }, 93 | { 94 | "type": "usage2", 95 | "currency": "PRS", 96 | "price": "5", 97 | "termtext": "商用授权许可,用于营利、 商业或经营目的转载、 引用、结集。如:\\n- 用于网站、软件、移动应用、社交媒体、邮件、电子书\\n- 用于印刷媒体如 杂志、 报纸、 书籍\\n- 用于广告、市场活动、宣传推广用途的内容\\n你可以全文或部分转载你购买的文本内容,或在不改变作者原意和立场的情况下编辑文本。任何情况下必须标明原文作者及出处。不标记原作者或出处通常会被视为剽窃行为。" 98 | } 99 | ], 100 | "receiver": "becd34540fefeab83730ffb479e98ee12fa1337e", 101 | "rId": "6ed18d21690433f0410a20bdf5e829975e0da840b48c3cb8e962b17138f5288e", 102 | "total": 5 103 | } 104 | } 105 | ``` 106 | 107 | 108 | ## build and deploy 109 | 110 | Run the service with prod env 111 | 112 | ``` 113 | lein with-profile prod ring server 114 | ``` 115 | 116 | Build the service with prod env 117 | 118 | ``` 119 | lein with-profile prod ring uberjar 120 | 121 | java -jar target/prsc-0.1.1-SNAPSHOT-standalone.jar 122 | ``` 123 | 124 | ## for docker testing 125 | 126 | docker build -t dh.press.one/pressone/prsc . 127 | docker run --rm -e apiroot=https://dev.press.one/api/v2 --name prsc -p 3001:3001 dh.press.one/pressone/prsc 128 | -------------------------------------------------------------------------------- /src/prsc/verify.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.verify 2 | "verify functions" 3 | (:require [config.core :refer [env]] 4 | [cheshire.core :refer :all] 5 | [instaparse.core :as insta] 6 | [clara.rules :refer :all] 7 | [clojure.tools.logging :as log] 8 | [schema.core :as s] 9 | [prsc.dsl :refer :all] 10 | [prsc.mixin :refer :all] 11 | [prsc.assetmapping :refer :all] 12 | [prsc.error :refer :all])) 13 | 14 | (def enabled-input-fields ["license-type"]) 15 | 16 | (defn fieldmapping [field] 17 | (let [value ((keyword field) 18 | {:license "license-type"})] 19 | (if (nil? value) 20 | field 21 | value))) 22 | 23 | (defn verify-PRSdata [data] 24 | (if (= true (or (nil? (:meta data)) (nil? (:data data)))) 25 | nil ;TOFIX error format 26 | {:type (:type data) :meta (parse-string (:meta data) true) :data (parse-string (:data data) true)})) 27 | 28 | (defn fetch-PRSC [data] 29 | (let [user_address (:user_address data) 30 | meta (:meta data)] 31 | (let [undecoded_script (prsc.utils/safenth (:uris (parse-string meta true)) 0)] 32 | (let [start (clojure.string/index-of undecoded_script "PRSC")] 33 | (java.net.URLDecoder/decode (subs undecoded_script start)))))) 34 | 35 | (defn parse-get-license [result] 36 | (let [address (:address (:?receiver (first result))) 37 | currency (:currency (:?license (first result))) 38 | price (:price (:?license (first result))) 39 | ] 40 | {:address address :currency currency :price price} 41 | ) 42 | ) 43 | (defn parseQueryResult [query-name result] 44 | (cond 45 | (= (name query-name) "get-license") (parse-get-license result) 46 | :else {}) 47 | ) 48 | 49 | (defn block-prsid-mapping [prsid blockreferences] 50 | (let [block ((keyword prsid) blockreferences)] 51 | (let [block_num (:block_num block) 52 | trx_id (:transaction_id block)] 53 | {:block_num block_num :trx_id trx_id} 54 | ) 55 | ) 56 | ) 57 | 58 | ;TOFIX unknown error... 59 | (defn txresult-to-data [tx] 60 | (let [data (prsc.eos/verifytrx tx)] 61 | (if (nil? data) 62 | (make-error nil PRS_DATA_IS_NULL) 63 | (let [prsdata (verify-PRSdata data)] 64 | (if (nil? prsdata) 65 | (make-error nil PRS_DATA_ERROR) 66 | prsdata))) 67 | ) 68 | ) 69 | 70 | (defn tx-to-bill [tx] 71 | (let [data (txresult-to-data tx)] 72 | (if (nil? (:err data)) 73 | (if (= "BILL:2" (:type data)) 74 | data 75 | {} 76 | ) 77 | data 78 | ) 79 | 80 | ) 81 | ) 82 | 83 | (defn verify-payment [provider wallet_id snapshot_id trace_id viewtoken amount currency] 84 | (log/debug "==========verify payment=============") 85 | (log/debug provider) 86 | (log/debug wallet_id) 87 | (log/debug snapshot_id) 88 | (log/debug trace_id) 89 | (log/debug viewtoken) 90 | (log/debug amount) 91 | (log/debug currency) 92 | 93 | (let [asset-id (try 94 | ((keyword currency) (eval (symbol (str "prsc.assetmapping/" provider)))) 95 | (catch Exception e (log/error (str "verify-payment " e))))] 96 | (log/debug (str "asset-id:" asset-id)) 97 | (cond 98 | (= provider "mixin.one") (verifyMixinTx snapshot_id viewtoken trace_id) 99 | :else (make-error nil UNSUPPORTED_PAYMENT_SERVICE_ERROR {:provider provider}) 100 | ) 101 | ) 102 | ) 103 | 104 | (defn verify-contract-bill-receipt [ receipt_tx bill_tx contract_result] 105 | (let [view_token (:payment_view_token (:data receipt_tx)) 106 | snapshot_id (:payment_snapshot_id (:data receipt_tx)) 107 | receipt_amount (:amount (:meta receipt_tx)) 108 | contract_id (:contract_id (:meta receipt_tx)) 109 | receipt_beneficiary_address (:beneficiary_address (:meta receipt_tx)) 110 | receipt_payer_address (:payer_address (:meta receipt_tx)) 111 | receipt_beneficiary_wallet_id (:beneficiary_wallet_id (:meta receipt_tx)) 112 | ] 113 | 114 | (log/debug (str "view token:" view_token)) 115 | (log/debug (str "snapshot_id:" snapshot_id)) 116 | (log/debug (str "bill_tx" bill_tx)) 117 | ;0 verify address and wallet 118 | (if (true? (and (= (:beneficiary_address (:meta bill_tx)) receipt_beneficiary_address) (= (:beneficiary_wallet_id (:meta bill_tx)) receipt_beneficiary_wallet_id))) ;0 VERIFY address/wallet of the receipt object match with bill object 119 | (if (true? (and (= (:address contract_result) receipt_beneficiary_address) (= (:price contract_result) receipt_amount))) ;1 verify address/amount of contract query result match with receipt 120 | (verify-payment (:payment_provider (:data bill_tx)) (:beneficiary_wallet_id (:meta bill_tx)) snapshot_id (:payment_trace_id (:data bill_tx)) view_token receipt_amount (:currency contract_result)) 121 | (make-error nil RECEIPT_CONTRACT_PAYMENT_NOT_MATCH_ERROR {:contract_address (:address contract_result) :receipt_beneficiary_address receipt_beneficiary_address :contract_price (:price contract_result) :receipt_amount receipt_amount}) 122 | ) 123 | (make-error nil RECEIPT_BENEFICIARY_ADDRESS_WALLET_NOT_MATCH_ERROR {:beneficiary_address (:beneficiary_address (:meta bill_tx)) :receipt_beneficiary_address receipt_beneficiary_address :beneficiary_wallet_id (:beneficiary_wallet_id (:meta bill_tx)) :receipt_beneficiary_wallet_id receipt_beneficiary_wallet_id }) 124 | ) 125 | ) 126 | ;0: bill == receipt 127 | ;0.1: receipt == contract 128 | ;1: mixin trace_id == bill_trace_id 129 | ;2: mixin == bill == receipt 130 | ) 131 | 132 | (defn parse-PRSData [data] 133 | (let [ 134 | bill_id (:bill_id (:data data)) 135 | references (:references (:meta data)) 136 | args (filter (fn [[k v]] (some #(= (name k) %) enabled-input-fields)) (reduce merge (map (fn [x] {(keyword (fieldmapping (clojure.string/replace (name x) #"contract_" ""))) (x (:meta data))}) (filter (fn [x] (clojure.string/starts-with? (name x) "contract_")) (keys (:meta data)))))) 137 | ] 138 | (if (or (nil? bill_id) (nil? references)) 139 | (make-error nil PRS_DATA_ERROR) 140 | (let [bill_block (block-prsid-mapping bill_id references)] 141 | (if (or (nil? (:block_num bill_block)) (nil? (:trx_id bill_block))) 142 | (make-error nil BILL_DATA_ERROR {:bill_id bill_id}) 143 | (let [tx (prsc.eos/get-eos-tx (:block_num bill_block) (:trx_id bill_block))] 144 | (if (nil? (:err tx)) 145 | (let [billtx (tx-to-bill tx)] 146 | (let [contract_id (:contract_id (:meta billtx))] 147 | (log/debug (str "contract_id " contract_id)) 148 | (let [contract_block (block-prsid-mapping contract_id references)] 149 | (log/debug (str "contract_block " contract_block)) 150 | (if (or (nil? (:block_num contract_block)) (nil? (:trx_id contract_block))) 151 | (make-error nil CONTRACT_DATA_ERROR {:contract_id contract_id}) 152 | (let [contract_tx (prsc.eos/get-eos-tx (:block_num contract_block) (:trx_id contract_block))] 153 | (let [contract_data (prsc.eos/verifytrx contract_tx)] 154 | (if (nil? contract_data) 155 | (make-error nil PRS_NOCONTRACT_ERROR {:block_num (:block_num contract_block) :trx_id (:trx_id contract_block)}) 156 | (try 157 | (let [queryresult (prsc.dsl/run-PRSC (fetch-PRSC contract_data) args prsc.dsl/get-license)] 158 | (let [queryresult (parseQueryResult (:name (meta #'prsc.dsl/get-license)) queryresult)] 159 | (verify-contract-bill-receipt data billtx queryresult))) ;TOFIX why throw 9006 error? 160 | (catch Exception e (log/error (str "parse-PRSData" (.getMessage e))) PRS_RUNCONTRACT_ERROR) ;TOFIX: (:err :reason) foramt 161 | ) 162 | ) 163 | ) 164 | ) 165 | ) 166 | ) 167 | ) 168 | ) 169 | tx ;return error 170 | ) 171 | ) 172 | ) 173 | ) 174 | ))) 175 | 176 | (defn verify-onchain [blocknum txid] 177 | (log/debug (str "verify-onchain" blocknum " " txid)) 178 | (let [tx (prsc.eos/get-eos-tx blocknum txid)] 179 | (if (prserr? tx) 180 | tx 181 | (let [data (prsc.eos/verifytrx tx)] 182 | (if (nil? data) 183 | (make-error nil PRS_DATA_IS_NULL ) 184 | (let [prsdata (verify-PRSdata data)] 185 | (if (nil? prsdata) 186 | (make-error nil PRS_DATA_ERROR) 187 | (parse-PRSData prsdata)) 188 | ))) 189 | ))) 190 | 191 | 192 | -------------------------------------------------------------------------------- /src/prsc/dsl.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.dsl 2 | "prsc dsl" 3 | (:require [config.core :refer [env]] 4 | [cheshire.core :refer :all] 5 | [instaparse.core :as insta] 6 | [clara.rules :refer :all] 7 | [schema.core :as s] 8 | [clojure.tools.logging :as log] 9 | [prsc.utils :refer :all] 10 | )) 11 | 12 | 13 | (defrecord LicenseTerm [type currency price termtext]) 14 | 15 | (defrecord ContractText [type text]) 16 | 17 | (defrecord Receiver [address]) 18 | 19 | (defrecord Deposit [service address currency price total unlock]) 20 | 21 | (defrecord multi-receiver [address currency p]) 22 | 23 | (defrecord Principals [address]) 24 | 25 | (defrecord license-type [name]) 26 | 27 | (defrecord Linked-license [address type currency price]) 28 | 29 | (defrecord Linked-result [result]) 30 | 31 | (defrecord Linked-Contract-List [rid args]) 32 | 33 | (defrecord Enabledfuncs [name]) 34 | 35 | (defn create-record [fnname args] 36 | (cond 37 | (= "license-type" fnname) (->license-type args) 38 | :else nil 39 | ) 40 | ) 41 | 42 | (defquery get-license 43 | "Returns the available license" 44 | [] 45 | [?receiver <- Receiver] 46 | [?license <- LicenseTerm]) 47 | 48 | (defquery get-linked-license 49 | "Returns the linked license" 50 | [] 51 | [?linkedlicenses <- Linked-license]) 52 | 53 | (defquery get-linked-result 54 | "Returns the linked result" 55 | [] 56 | [?linkedresult <- Linked-result]) 57 | 58 | (defquery get-license-type 59 | "Returns the current license type" 60 | [] 61 | [?licensetype <- license-type]) 62 | 63 | (defquery get-linked-Contracts 64 | "Returns the linked license type" 65 | [] 66 | [?linkedContracts <- Linked-Contract-List]) 67 | 68 | (defquery get-multireceivers 69 | "Returns multi receivers" 70 | [] 71 | [?MultiReceivers <- multi-receiver]) 72 | 73 | (defquery get-principals 74 | "Returns multi receivers" 75 | [] 76 | [?Principal <- Principals]) 77 | 78 | (defquery get-funcs 79 | "Returns multi receivers" 80 | [] 81 | [?Func <- Enabledfuncs]) 82 | 83 | (defquery get-deposit 84 | "Returns the despoit info" 85 | [] 86 | [?Deposit <- Deposit]) 87 | 88 | 89 | 90 | (defn formatdesc [& thedesc] 91 | [:desc (clojure.string/trim (apply str thedesc))]) 92 | 93 | (defn formatname [& thename] 94 | [:name (clojure.string/trim (apply str thename))]) 95 | 96 | 97 | (def prscparsertransform-options 98 | {:price (comp str read-string) 99 | :currency read-string 100 | :license (fn [thetype currency price termtext] [:license {"type" thetype, "currency" currency, "price" price, "termtext" (clojure.string/trim termtext)}]) 101 | :contract (fn [thetype text] [:contract {"name" thetype, "text" text}]) 102 | :name formatname 103 | :desc formatdesc}) 104 | ;args nl 105 | 106 | 107 | (defn print-ver [input] 108 | (log/info (str "version: " input)) 109 | ) 110 | 111 | (defn process-deposit [depositservice address currency price total unlock] 112 | (if (and (= "XIN" depositservice) (uuid? (java.util.UUID/fromString address))) 113 | (insert! (->Deposit depositservice address currency price total unlock)) 114 | (throw (Exception. "deposit service or address format error.")))) 115 | 116 | (defn parsemultireceiver [& receiverlist] 117 | ;verify: not more than 100% 118 | (let [sumshares (apply + (map (fn [x] 119 | (read-string (if (= 3 (count x)) (clojure.string/replace (nth x 2 "0") #"%" "") "0"))) (first receiverlist)))] 120 | (if-not 121 | (= 0.0 (- 100.00 sumshares)) 122 | (throw (Exception. (str "the sum of receiver shares should equal 100%, current: " sumshares "%"))) 123 | (doall (map (fn [x] 124 | (if (= 3 (count x)) 125 | (insert! (->multi-receiver (nth x 1 "0") "nil" (nth x 2 "0"))) 126 | (insert! (->multi-receiver (nth x 1 "0") (nth x 2 "0") (nth x 3 "0"))))) (first receiverlist)))))) 127 | 128 | (defn process-principals [& principallist] 129 | (doall (map (fn [x] 130 | (insert! (->Principals x))) (first principallist)))) 131 | 132 | (defn process-funcs [& funcs] 133 | (doall (map (fn [x] 134 | (log/info (str "process-funcs: " x)) 135 | (insert! (->Enabledfuncs x))) (first funcs)))) 136 | 137 | 138 | (def prscparser 139 | (insta/parser 140 | " = [version | assign | receiver | license | contract | pay | desc | use | holder | multireceiver | name | deposit | principals | func]+ 141 | version = <'PRSC Ver'> space vernum; 142 | vernum = float; 143 | assign = <'let'> varname <'='> [ address | string | utf8str ] nl 144 | receiver = <'Receiver'> address 145 | name = <'Name'> space utf8stre 146 | multireceiver = <'MultiReceiver'> receiver_info+ 147 | holder = <'Holder'> address 148 | deposit = <'Deposit'> depositservice <':'> string <'Principal'> currency <':'> price <'Total'> <':'> digit <'Unlock'> <':'> digit 149 | desc = <'Desc'> space utf8stre 150 | principals = <'Principals'> space address+ 151 | func = <'Func'> space string+ 152 | license = <'License'> licensetype currency <':'> price <'Terms'> <':'> [ address | string | utf8stre ] 153 | contract = <'Contract'> licensetype [ address | string | utf8stre ] 154 | pay = <'Pay'> msghash licensetype currency <':'> price 155 | receiver_info = address [(currency <':'> price) | percent] 156 | use = <'Use'> rid args 157 | price = float; 158 | = #'[0-9]\\d?(?:\\.\\d{1,2})?%'; 159 | = 'PRS' | 'CNB'; 160 | = 'XIN'; 161 | = string; 162 | = utf8str; 163 | = #'[A-Za-z0-9_-]+'; 164 |
= #'[A-Za-z0-9]{40}'; 165 | = #'[A-Za-z0-9]{64}'; 166 | = #'[A-Za-z0-9]{64}'; 167 | = string; 168 | = <#'[ ]+'>; 169 | = #'([^\r\n\"\\\\]|\\s\\\\.,)+'; 170 | = #'([^\r\n\"]|\\s\\\\.,)+'; 171 | = #'[0-9]+(\\.[0-9]+)?'; 172 | = #'[0-9]+'; 173 | = #'[\r\n]+'; 174 | " 175 | :auto-whitespace :standard)) 176 | 177 | (def prsctransform-options 178 | {:vernum read-string 179 | :version (fn [ver] 180 | {:name "version" 181 | :lhs () 182 | :rhs `(print-ver ~ver)}) 183 | :name (fn [& thename] 184 | {:name "name" 185 | :lhs () 186 | :rhs ()}) 187 | :desc (fn [& thedesc] 188 | {:name "desc" 189 | :lhs () 190 | :rhs ()}) 191 | :contract (fn [thetype text] 192 | {:name (str "contract_" thetype) 193 | :lhs [{:type license-type 194 | :constraints [(list = (symbol "name") thetype)]}] 195 | :rhs `(insert! (->ContractText ~thetype ~text))}) 196 | :deposit (fn [depositservice address currency price total unlock] 197 | {:name "deposit" 198 | :lhs () 199 | :rhs `(process-deposit ~depositservice ~address ~currency ~price ~total ~unlock)}) 200 | :principals (fn [& principallist] 201 | {:name "principals" 202 | :lhs () 203 | :rhs `(process-principals (apply list '~principallist))}) 204 | :receiver (fn [address] 205 | {:name "receiver" 206 | :lhs () 207 | :rhs `(insert! (->Receiver ~address))}) 208 | :multireceiver (fn [& receiverlist] 209 | {:name "multireceiver" 210 | :lhs () 211 | :rhs `(parsemultireceiver (apply list '~receiverlist))}) 212 | :price (comp str read-string) 213 | :currency read-string 214 | :depositservice read-string 215 | :use (fn [rid args] 216 | {:name rid 217 | :lhs [] 218 | :rhs `(insert! (->Linked-Contract-List ~rid ~args))}) 219 | :func (fn [& funclist] 220 | {:name "func" 221 | :lhs () 222 | :rhs `(process-funcs (apply list '~funclist))}) 223 | :license (fn [thetype currency price termtext] 224 | {:name thetype 225 | :lhs [{:type license-type 226 | :constraints [(list = (symbol "name") thetype)]}] 227 | :rhs `(insert! (->LicenseTerm ~thetype ~currency ~price ~(clojure.string/trim termtext)))})}) 228 | 229 | (s/defn ^:always-validate load-user-rules :- [clara.rules.schema/Production] 230 | [business-rules :- s/Str] 231 | 232 | (let [parse-tree (prscparser business-rules)] 233 | 234 | (when (insta/failure? parse-tree) 235 | (throw (ex-info ( print-str parse-tree) {:failure parse-tree}))) 236 | 237 | (insta/transform prsctransform-options parse-tree))) 238 | 239 | 240 | (defn run-PRSC [contractcode args resultfn] 241 | (let [session (mk-session 'prsc.dsl (load-user-rules contractcode))] 242 | (let [inserted-session (apply insert (into [] (concat (list session) (map (fn [input] (create-record (name (input 0)) (input 1))) args))))] 243 | (let [fired-session (fire-rules inserted-session)] 244 | (query fired-session resultfn)))) 245 | ) 246 | -------------------------------------------------------------------------------- /src/prsc/core.clj: -------------------------------------------------------------------------------- 1 | (ns prsc.core 2 | (:gen-class) 3 | (:require [prsc.utils :refer :all] 4 | [prsc.crypto :refer :all] 5 | [prsc.mixin :refer :all] 6 | [prsc.eos :refer :all] 7 | [prsc.dsl :refer :all] 8 | [prsc.verify :refer :all] 9 | [prsc.error :refer :all] 10 | [config.core :refer [env]] 11 | [clojure.data.json :as json] 12 | [clojure.tools.logging :as log] 13 | [compojure.api.sweet :refer :all] 14 | [compojure.route :as route] 15 | [compojure.api.exception :as ex] 16 | [ring.util.http-response :refer :all :as response] 17 | [org.httpkit.client :as http] 18 | [instaparse.core :as insta] 19 | [clara.rules :refer :all] 20 | [schema.core :as s] 21 | [cheshire.core :refer :all])) 22 | 23 | 24 | (def globalvar (atom {})) 25 | 26 | (defn get-global [key] 27 | (@globalvar key)) 28 | 29 | (defn update-global [key val] 30 | (swap! globalvar assoc key val)) 31 | 32 | (defn prscparse [input] 33 | (->> (prscparser input) (insta/transform prsctransform-options))) 34 | 35 | (defn -main [& args] 36 | ;; (update-global :level 0) 37 | (if (= true ((complement nil?) (first *command-line-args*))) 38 | 39 | ;;(let [arg (clojure.string/trim (first *command-line-args*))] 40 | ;; (if (= "createkeys" arg) 41 | ;; (println (test-sign "test crypto")) 42 | ;; (System/exit 0) 43 | ;;)) 44 | (let [rid (clojure.string/trim (first *command-line-args*))] 45 | (let [contractcode (cond (= 64 (count rid)) 46 | (fetchContractCodeWithrID rid) 47 | :else (slurp rid))] 48 | (try 49 | (let [session (-> (mk-session 'prsc.dsl (load-user-rules contractcode)) 50 | (insert 51 | (->license-type "Commercial") 52 | ;(->Linked-license "add01" "p01" "PRS" "1.1111") 53 | ;(->Linked-license "add02" "p02" "PRS" "2.1111") 54 | ) 55 | (fire-rules))] 56 | (let [result (query session get-license)] 57 | (clojure.pprint/pprint result)) 58 | (let [result (apply query [session get-linked-Contracts])] 59 | (clojure.pprint/pprint result)) 60 | (let [result (apply query [session get-multireceivers])] 61 | (clojure.pprint/pprint result)) 62 | (let [result (apply query [session get-deposit])] 63 | (clojure.pprint/pprint result)) 64 | (let [result (apply query [session get-principals])] 65 | (clojure.pprint/pprint result))) 66 | 67 | (catch Exception e 68 | (clojure.pprint/pprint "===error") 69 | (clojure.pprint/pprint e)) 70 | (finally 71 | (printf "/license level: %s%n" (get-global :level)))) 72 | 73 | (let [parse-tree (prscparser contractcode)] 74 | (clojure.pprint/pprint (insta/transform prscparsertransform-options parse-tree))))))) 75 | 76 | (defn format-for-json [k] 77 | (apply hash-map (assoc k 0 (keyword (clojure.string/replace (first k) #":\?" ""))))) 78 | 79 | (defn parseArgs [argstr] 80 | (let [args (clojure.string/split argstr #",")] 81 | args)) 82 | 83 | (def app 84 | (api 85 | {:api {:invalid-routes-fn nil}} 86 | (context "/v2" [] 87 | :tags ["v2"] 88 | 89 | (GET "/echo" [] 90 | :query-params [q :- String] 91 | 92 | ;(log/error "error /echo") 93 | ;(log/debug "debug /echo") 94 | ;(log/info "info /echo") 95 | (try 96 | (let [signtext (str "/echo" "$" q "$" q)] 97 | (let [result {:?result (Integer/parseInt q)}] 98 | (update-header (update-header (ok (format-for-json (first result))) "signtext" (fn [arg] (-> signtext))) "sig" (fn [arg] (-> (signature (apply str signtext))))))) 99 | 100 | (catch Exception e 101 | (log/error (str "GET /echo" e)) 102 | (ok (->> (ex-data e)))) 103 | (finally (log/info "/echo response")) 104 | )) 105 | 106 | (GET "/verify/:block_num/:tx_id" [] 107 | :path-params [block_num :- String tx_id :- String] 108 | :query-params [session :- String] 109 | (try 110 | (let [signtext (str "/verify" "$" block_num "$" tx_id "$" session)] 111 | (let [result (verify-onchain block_num tx_id)] 112 | (if (= true (instance? Boolean (:result result))) 113 | (update-header (update-header (ok (assoc result :session session)) "signtext" (fn [arg] (-> signtext))) "sig" (fn [arg] (-> (signature (apply str signtext))))) 114 | (update-header (update-header (ok (assoc result :session session)) "signtext" (fn [arg] (-> signtext))) "sig" (fn [arg] (-> (signature (apply str signtext))))) 115 | ))) 116 | 117 | (catch Exception e 118 | (log/error (str "GET /verify" e)) 119 | (ok (->> (ex-data e)))) 120 | (finally 121 | (log/debug "GET /verify done")))) 122 | (GET "/license/:address" [] 123 | :path-params [address :- String] 124 | :query-params [licensetype :- String] 125 | (try 126 | (let [contractcode (fetchContractCodeWithrID address)] 127 | (let [session (-> (mk-session 'prsc.dsl (load-user-rules contractcode)) 128 | (insert 129 | (->license-type licensetype)) 130 | (fire-rules))] 131 | (let [result (query session get-license)] 132 | (let [result-linkedcontracts (query session get-linked-Contracts)] 133 | (let [newmap (doall 134 | (map (fn [k] 135 | (format-for-json k)) (first result)))] 136 | (let [signtext (for [{{address :address} :?receiver {type :type currency :currency price :price termtext :termtext} :?license} result] 137 | (str "/license/" address "$" licensetype "$" "address,type,currency,price" address type currency price))] 138 | (update-header (update-header (ok newmap) "signtext" (fn [arg] (-> signtext))) "sig" (fn [arg] (-> (signature (apply str signtext))))))))))) 139 | 140 | (catch Exception e 141 | (log/error (str "GET /license " e)) 142 | (ok (->> (ex-data e)))) 143 | (finally 144 | (log/debug "GET /license done")))) 145 | (GET "/test" [] 146 | (ok (str "{'message':'ok'}"))) 147 | (GET "/parser" [] 148 | :query-params [address :- String] 149 | (try 150 | (let [contractcode (fetchContractCodeWithrID address)] 151 | (let [parse-tree (prscparser contractcode)] 152 | (when (insta/failure? parse-tree) 153 | (throw (ex-info (print-str parse-tree) {:failure parse-tree}))) 154 | (ok (->> parse-tree (insta/transform prscparsertransform-options))))) 155 | 156 | (catch Exception e 157 | (log/error (str "GET /parser " e)) 158 | (ok (->> (ex-data e)))) 159 | (finally 160 | (log/debug "GET /parser done")))) 161 | 162 | (POST "/repl" [] 163 | :body-params [code :- String, 164 | call :- String, 165 | args :- String] 166 | 167 | (try 168 | (if (= call "") 169 | (let [parse-tree (prscparser code)] 170 | (when (insta/failure? parse-tree) 171 | (throw (ex-info (print-str parse-tree) {:failure parse-tree}))) 172 | (ok (->> parse-tree (insta/transform prscparsertransform-options)))) 173 | (let [session (-> (mk-session 'prsc.dsl (load-user-rules code)) 174 | (insert 175 | (->license-type args)) 176 | (fire-rules))] 177 | (let [result (apply query [session (str (symbol "prsc.dsl" call))])] 178 | (let [newmap (doall 179 | (map (fn [k] 180 | (format-for-json k)) (first result)))] 181 | 182 | (ok newmap))))) 183 | (catch Exception e 184 | (log/error (str "POST /repl " e)) 185 | (ok (->> (ex-data e)))) 186 | (finally 187 | (log/debug "GET /repl response"))))) 188 | 189 | 190 | ;(GET "/license/:address" [] 191 | ; :path-params [address :- String] 192 | ; :query-params [licensetype :- String] 193 | ; (try 194 | ; (let [contractcode (fetchContractCodeWithrID address)] 195 | ; (let [session (-> (mk-session 'prsc.dsl (load-user-rules contractcode)) 196 | ; (insert 197 | ; (->license-type licensetype)) 198 | ; (fire-rules))] 199 | ; (let [result (query session get-license)] 200 | ; (let [result-linkedcontracts (query session get-linked-Contracts)] 201 | ; (let [newmap (doall 202 | ; (map (fn [k] 203 | ; (format-for-json k)) (first result)))] 204 | ; (let [newmap1 (doall 205 | ; (map (fn [k] 206 | ; (format-for-json k)) (first result-linkedcontracts)))] 207 | ; (ok (concat newmap newmap1)))))))) 208 | 209 | ; (catch Exception e 210 | ; (log/error (str "GET /license/:address " e)) 211 | ; (ok (->> (ex-data e)))) 212 | ; (finally 213 | ; (log/debug "GET /license/:address done")))) 214 | 215 | ;(GET "/parser" [] 216 | ; :query-params [address :- String] 217 | ; (try 218 | ; (let [contractcode (fetchContractCodeWithrID address)] 219 | ; (let [parse-tree (prscparser contractcode)] 220 | ; (when (insta/failure? parse-tree) 221 | ; (throw (ex-info (print-str parse-tree) {:failure parse-tree}))) 222 | ; (ok (->> parse-tree (insta/transform prscparsertransform-options))))) 223 | 224 | ; (catch Exception e 225 | ; (log/error (str "GET /parser " e)) 226 | ; (ok (->> (ex-data e)))) 227 | ; (finally 228 | ; (log/debug "GET /parser done")))) 229 | 230 | 231 | (undocumented 232 | (route/resources "/") 233 | (route/not-found 234 | "404 Not Found")))) 235 | --------------------------------------------------------------------------------