From 156d4fd24c803c86243aa45dd1a075e3f85c731b Mon Sep 17 00:00:00 2001 From: Vitaliy Vlasov Date: Wed, 18 Apr 2018 21:06:00 +0300 Subject: [PATCH 01/85] Initial changes for comment updates and state transitions --- resources/sql/queries.sql | 59 ++---- src/clj/commiteth/bounties.clj | 44 +--- src/clj/commiteth/db/bounties.clj | 25 +-- src/clj/commiteth/db/core.clj | 28 +++ src/clj/commiteth/db/issues.clj | 23 +-- src/clj/commiteth/github/core.clj | 115 ++++++----- src/clj/commiteth/routes/services.clj | 16 +- src/clj/commiteth/scheduler.clj | 246 +++++++---------------- src/clj/commiteth/util/png_rendering.clj | 16 -- src/clj/commiteth/util/util.clj | 8 + 10 files changed, 220 insertions(+), 360 deletions(-) diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index 81316d8..c17641b 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -267,7 +267,7 @@ SELECT i.tokens AS tokens, i.value_usd AS value_usd, u.login AS winner_login, - u.address AS payout_address + u.address AS winner_address FROM issues i, pull_requests p, users u, repositories r WHERE p.issue_id = i.issue_id @@ -322,27 +322,23 @@ AND i.payout_hash IS NOT NULL; -- :name update-confirm-hash :! :n -- :doc updates issue with confirmation hash UPDATE issues -SET confirm_hash = :confirm_hash, +SET confirm_hash = :confirm-hash, updated = timezone('utc'::text, now()) -WHERE issue_id = :issue_id; +WHERE issue_id = :issue-id; --- :name update-execute-hash :! :n --- :doc updates issue with execute transaction hash +-- :name update-execute-hash-and-winner-login :! :n +-- :doc updates issue with execute transaction hash and winner login UPDATE issues -SET execute_hash = :execute_hash, +SET execute_hash = :execute-hash, +winner_login = :winner-login, updated = timezone('utc'::text, now()) -WHERE issue_id = :issue_id; - --- :name update-winner-login :! :n -UPDATE issues -SET winner_login = :winner_login -WHERE issue_id = :issue_id; +WHERE issue_id = :issue-id; -- :name update-watch-hash :! :n -- :doc updates issue with watch transaction hash UPDATE issues -SET watch_hash = :watch_hash -WHERE issue_id = :issue_id; +SET watch_hash = :watch-hash +WHERE issue_id = :issue-id; -- :name pending-watch-calls :? :* -- :doc issues with a pending watch transaction @@ -356,9 +352,9 @@ WHERE watch_hash IS NOT NULL; -- :name update-payout-hash :! :n -- :doc updates issue with payout transaction hash UPDATE issues -SET payout_hash = :payout_hash, +SET payout_hash = :payout-hash, updated = timezone('utc'::text, now()) -WHERE issue_id = :issue_id; +WHERE issue_id = :issue-id; -- :name reset-payout-hash :! :n -- :doc sets issue's payout transaction hash to NULL @@ -371,26 +367,9 @@ WHERE issue_id = :issue_id; -- :name update-payout-receipt :! :n -- :doc updates issue with payout transaction receipt UPDATE issues -SET payout_receipt = :payout_receipt::jsonb, +SET payout_receipt = :payout-receipt::jsonb, updated = timezone('utc'::text, now()) -WHERE issue_id = :issue_id; - - --- :name update-token-balances :! :n --- :doc updates issue with given token balances -UPDATE issues -SET tokens = :token_balances::jsonb, -updated = timezone('utc'::text, now()) -WHERE contract_address = :contract_address; - - --- :name update-usd-value :! :n --- :doc updates issue with given USD value -UPDATE issues -SET value_usd = :usd_value, -value_usd_updated = timezone('utc'::text, now()) -WHERE contract_address = :contract_address; - +WHERE issue_id = :issue-id; -- :name update-issue-open :! :n -- :doc updates issue's open status @@ -535,17 +514,19 @@ SELECT r.owner AS owner, r.repo AS repo FROM issues i, repositories r -WHERE i.issue_number = :issue_number +WHERE i.issue_number = :issue-number AND r.repo_id = i.repo_id AND r.owner = :owner AND r.repo = :repo; --- :name update-eth-balance :! :n +-- :name update-balances :! :n -- :doc updates balance of a wallet attached to a given issue UPDATE issues -SET balance_eth = :balance_eth, +SET balance_eth = :balance-eth, + tokens = :token-balances::jsonb, + value_usd = :usd-value, updated = timezone('utc'::text, now()) -WHERE contract_address = :contract_address; +WHERE contract_address = :contract-address; -- :name save-issue-comment-image! :> res + (map #(vector (keyword (str/replace (name (first %1)) "_" "-")) + (second %1))) + (into {}))) + +(defn result-one-sql->lisp + [this result options] + (convert-keys-to-lisp-case (first result))) + +(defn result-many-sql->lisp + [this result options] + (map convert-keys-to-lisp-case result)) + +(defmethod hugsql.core/hugsql-result-fn :1 [sym] + 'commiteth.db.core/result-one-sql->lisp) + +(defmethod hugsql.core/hugsql-result-fn :one [sym] + 'commiteth.db.core/result-one-sql->lisp) + +(defmethod hugsql.core/hugsql-result-fn :* [sym] + 'commiteth.db.core/result-many-sql->lisp) + +(defmethod hugsql.core/hugsql-result-fn :many [sym] + 'commiteth.db.core/result-many-sql->lisp) + diff --git a/src/clj/commiteth/db/issues.clj b/src/clj/commiteth/db/issues.clj index fe4b6ce..806828c 100644 --- a/src/clj/commiteth/db/issues.clj +++ b/src/clj/commiteth/db/issues.clj @@ -1,6 +1,7 @@ (ns commiteth.db.issues (:require [commiteth.db.core :refer [*db*] :as db] [clojure.java.jdbc :as jdbc] + [commiteth.util.util :refer [to-db-map]] [clojure.set :refer [rename-keys]])) (defn create @@ -63,23 +64,13 @@ (jdbc/with-db-connection [con-db *db*] (db/list-pending-deployments con-db))) -(defn update-eth-balance - [contract-address balance-eth] +(defn update-balances + [contract-address balance-eth token-balances usd-value] (jdbc/with-db-connection [con-db *db*] - (db/update-eth-balance con-db {:contract_address contract-address - :balance_eth balance-eth}))) - -(defn update-token-balances - [contract-address balances] - (jdbc/with-db-connection [con-db *db*] - (db/update-token-balances con-db {:contract_address contract-address - :token_balances balances}))) - -(defn update-usd-value - [contract-address usd-value] - (jdbc/with-db-connection [con-db *db*] - (db/update-usd-value con-db {:contract_address contract-address - :usd_value usd-value}))) + (db/update-balances con-db (to-db-map contract-address + balance-eth + token-balances + usd-value)))) (defn update-open-status [issue-id is-open] diff --git a/src/clj/commiteth/github/core.clj b/src/clj/commiteth/github/core.clj index a121461..9e293d9 100644 --- a/src/clj/commiteth/github/core.clj +++ b/src/clj/commiteth/github/core.clj @@ -14,6 +14,10 @@ [digest :refer [sha-256]] [clojure.tools.logging :as log] [cheshire.core :as json] + [commiteth.util.png-rendering :as png-rendering] + [commiteth.db.issues :as db-issues] + [commiteth.db.bounties :as db-bounties] + [commiteth.db.comment-images :as comment-images] [clojure.string :as str]) (:import [java.util UUID])) @@ -271,13 +275,6 @@ (learn-more-text)) eth-balance-str payee-login)) - -(defn post-deploying-comment - [owner repo issue-number tx-id] - (let [comment (generate-deploying-comment owner repo issue-number tx-id)] - (log/info "Posting comment to" (str owner "/" repo "/" issue-number) ":" comment) - (issues/create-comment owner repo issue-number comment (self-auth-params)))) - (defn make-patch-request [end-point positional query] (let [{:keys [auth oauth-token] :as query} query @@ -296,54 +293,70 @@ :otp))] (assoc req :body (json/generate-string (or raw-query proper-query))))) +(defn update-bounty-comment-image [issue-id owner repo issue-number contract-address eth-balance eth-balance-str tokens] + (let [hash (github-comment-hash owner repo issue-number eth-balance) + issue-url (str owner "/" repo "/issues/" (str issue-number)) + png-data (png-rendering/gen-comment-image + contract-address + eth-balance-str + tokens + issue-url)] + (log/debug "update-bounty-comment-image" issue-id owner repo issue-number) + (log/debug contract-address eth-balance-str) + (log/debug "hash" hash) + + (if png-data + (comment-images/save-image! issue-id hash png-data) + (log/error "Failed ot generate PNG")))) + (defn update-comment "Update comment for an open bounty issue" - [owner repo comment-id issue-number contract-address eth-balance eth-balance-str tokens] - (let [comment (generate-open-comment owner - repo - issue-number - contract-address - eth-balance - eth-balance-str - tokens)] + [{:keys [issue-id owner repo comment-id issue-number contract-address + eth-balance eth-balance-str tokens + payout-receipt + winner-login winner-address transaction-hash] :as issue}] + (let [state (cond (nil? comment-id) :deployed + (not (nil? payout-receipt)) :paid + (not (str/blank? winner-login)) :merged + :else :open) + comment (cond (= state :deployed) + (generate-deploying-comment owner repo issue-number transaction-hash) + (= state :open) + (generate-open-comment owner + repo + issue-number + contract-address + eth-balance + eth-balance-str + tokens) + (= state :merged) + (generate-merged-comment contract-address + eth-balance-str + tokens + winner-login + (str/blank? winner-address)) + (= state :paid) + (generate-paid-comment contract-address + eth-balance-str + tokens + winner-login) + )] + (when (= :paid state) + (db-bounties/update-payout-receipt issue-id payout-receipt)) + + (when (= :open state) + (update-bounty-comment-image issue-id owner repo issue-number contract-address eth-balance eth-balance-str tokens)) + (log/debug (str "Updating " owner "/" repo "/" issue-number " comment #" comment-id " with contents: " comment)) - (let [req (make-patch-request "repos/%s/%s/issues/comments/%s" - [owner repo comment-id] - (assoc (self-auth-params) :body comment))] - (tentacles/safe-parse (http/request req))))) - - - -(defn update-merged-issue-comment - "Update comment for a bounty issue with winning claim (waiting to be - signed off by maintainer/user ETH address missing)" - [owner repo comment-id contract-address eth-balance-str tokens winner-login winner-address-missing?] - (let [comment (generate-merged-comment contract-address - eth-balance-str - tokens - winner-login - winner-address-missing?)] - (log/debug (str "Updating merged bounty issue (" owner "/" repo ")" - " comment#" comment-id " with contents: " comment)) - (let [req (make-patch-request "repos/%s/%s/issues/comments/%s" - [owner repo comment-id] - (assoc (self-auth-params) :body comment))] - (tentacles/safe-parse (http/request req))))) - -(defn update-paid-issue-comment - "Update comment for a paid out bounty issue" - [owner repo comment-id contract-address eth-balance-str tokens payee-login] - (let [comment (generate-paid-comment contract-address - eth-balance-str - tokens - payee-login)] - (log/debug (str "Updating paid bounty (" owner "/" repo ")" - " comment#" comment-id " with contents: " comment)) - (let [req (make-patch-request "repos/%s/%s/issues/comments/%s" - [owner repo comment-id] - (assoc (self-auth-params) :body comment))] - (tentacles/safe-parse (http/request req))))) + (if (= state :deployed) + (let [resp (issues/create-comment owner repo issue-number comment (self-auth-params)) + comment-id (:id resp)] + (db-issues/update-comment-id issue-id comment-id)) + (let [req (make-patch-request "repos/%s/%s/issues/comments/%s" + [owner repo comment-id] + (assoc (self-auth-params) :body comment))] + (tentacles/safe-parse (http/request req)))))) (defn get-issue [owner repo issue-number] diff --git a/src/clj/commiteth/routes/services.clj b/src/clj/commiteth/routes/services.clj index ce5d426..5e5e475 100644 --- a/src/clj/commiteth/routes/services.clj +++ b/src/clj/commiteth/routes/services.clj @@ -78,9 +78,7 @@ (into {})))) (defn top-hunters [] - (let [renames {:user_name :display-name - :avatar_url :avatar-url - :total_usd :total-usd}] + (let [renames {:user-name :display-name}] (map #(-> % (rename-keys renames) (update :total-usd usd-decimal->str)) @@ -107,16 +105,8 @@ (into {}) (assoc bounty :tokens))) renames {:user_name :display-name - :user_avatar_url :avatar-url - :issue_title :issue-title - :type :item-type - :repo_name :repo-name - :repo_owner :repo-owner - :issue_number :issue-number - :value_usd :value-usd - :claim_count :claim-count - :balance_eth :balance-eth - :user_has_address :user-has-address}] + :user-avatar-url :avatar-url + :type :item-type }] (map #(-> % (rename-keys renames) (update :value-usd usd-decimal->str) diff --git a/src/clj/commiteth/scheduler.clj b/src/clj/commiteth/scheduler.clj index 8400033..04d8136 100644 --- a/src/clj/commiteth/scheduler.clj +++ b/src/clj/commiteth/scheduler.clj @@ -4,6 +4,7 @@ [commiteth.eth.token-data :as token-data] [commiteth.github.core :as github] [commiteth.db.issues :as issues] + [commiteth.util.util :refer [to-map]] [taoensso.tufte :as tufte :refer (defnp p profiled profile)] [commiteth.db.bounties :as db-bounties] [commiteth.bounties :as bounties] @@ -11,6 +12,7 @@ [commiteth.util.util :refer [eth-decimal->str]] [clojure.tools.logging :as log] [mount.core :as mount] + [clojure.string :as str] [clj-time.core :as t] [clj-time.coerce :as time-coerce] [clj-time.periodic :refer [periodic-seq]] @@ -29,12 +31,10 @@ (profile {} (update-watch-hash)) (profile {} (update-payout-receipt)) (profile {} (update-contract-internal-balances)) - (profile {} (update-open-issue-usd-values)) (profile {} (update-balances)) (profile {} (doseq [i (range 5)] (update-contract-internal-balances) - (update-open-issue-usd-values) (update-balances))) ) @@ -44,39 +44,28 @@ [] (log/info "In update-issue-contract-address") (p :update-issue-contract-address - (doseq [{issue-id :issue_id - transaction-hash :transaction_hash} (issues/list-pending-deployments)] + (doseq [{:keys [issue-id transaction-hash]} (issues/list-pending-deployments)] (log/info "pending deployment:" transaction-hash) (try (when-let [receipt (eth/get-transaction-receipt transaction-hash)] (log/info "update-issue-contract-address: transaction receipt for issue #" issue-id ": " receipt) (if-let [contract-address (multisig/find-created-multisig-address receipt)] - (let [issue (issues/update-contract-address issue-id contract-address) - {owner :owner - repo :repo - comment-id :comment_id - issue-number :issue_number} issue + (let [{:keys [owner repo comment-id issue-number] :as issue} + (issues/update-contract-address issue-id contract-address) balance-eth-str (eth/get-balance-eth contract-address 6) - balance-eth (read-string balance-eth-str)] - (log/info "Updating comment image") - (bounties/update-bounty-comment-image issue-id - owner - repo - issue-number - contract-address - balance-eth - balance-eth-str - {}) + balance-eth (read-string balance-eth-str) + tokens {}] (log/info "Updating comment") - (github/update-comment owner - repo - comment-id - issue-number - contract-address - balance-eth - balance-eth-str - {})) + (github/update-comment (to-map issue-id + owner + repo + comment-id + issue-number + contract-address + balance-eth + balance-eth-str + tokens))) (log/error "Failed to find contract address in tx logs"))) (catch Throwable ex (do (log/error "update-issue-contract-address exception:" ex) @@ -88,11 +77,8 @@ "Under high-concurrency circumstances or in case geth is in defunct state, a bounty contract may not deploy successfully when the bounty label is addded to an issue. This function deploys such contracts." [] (p :deploy-pending-contracts - (doseq [{issue-id :issue_id - issue-number :issue_number - owner :owner - owner-address :owner_address - repo :repo} (db-bounties/pending-contracts)] + (doseq [{:keys [issue-id issue-number owner owner-address repo]} + (db-bounties/pending-contracts)] (log/debug "Trying to re-deploy failed bounty contract deployment, issue-id:" issue-id) (bounties/deploy-contract owner owner-address repo issue-id issue-number)))) @@ -101,54 +87,26 @@ [] (log/info "In self-sign-bounty") (p :self-sign-bounty - (doseq [{contract-address :contract_address - issue-id :issue_id - payout-address :payout_address - repo :repo - owner :owner - comment-id :comment_id - issue-number :issue_number - balance-eth :balance_eth - tokens :tokens - winner-login :winner_login} (db-bounties/pending-bounties)] - (try - (let [value (eth/get-balance-hex contract-address)] - (if (empty? payout-address) - (do - (log/error "Cannot sign pending bounty - winner has no payout address") - (github/update-merged-issue-comment owner - repo - comment-id - contract-address - (eth-decimal->str balance-eth) - tokens - winner-login - true)) - (let [execute-hash (multisig/send-all contract-address payout-address)] - (log/info "Payout self-signed, called sign-all(" contract-address payout-address ") tx:" execute-hash) - (db-bounties/update-execute-hash issue-id execute-hash) - (db-bounties/update-winner-login issue-id winner-login) - (github/update-merged-issue-comment owner - repo - comment-id - contract-address - (eth-decimal->str balance-eth) - tokens - winner-login - false)))) - (catch Throwable ex - (do (log/error "self-sign-bounty exception:" ex) - (clojure.stacktrace/print-stack-trace ex)))))) - (log/info "Exit self-sign-bounty") - ) + (doseq [{:keys [contract-address winner-address issue-id winner-login] :as issue} + (db-bounties/pending-bounties)] + (try + (let [value (eth/get-balance-hex contract-address)] + (when-not (empty? winner-address) + (let [execute-hash (multisig/send-all contract-address winner-address)] + (log/info "Payout self-signed, called sign-all(" contract-address winner-address ") tx:" execute-hash) + (db-bounties/update-execute-hash-and-winner-login issue-id execute-hash winner-login))) + (github/update-comment issue)) + (catch Throwable ex + (do (log/error "self-sign-bounty exception:" ex) + (clojure.stacktrace/print-stack-trace ex)))))) + (log/info "Exit self-sign-bounty")) (defn update-confirm-hash "Gets transaction receipt for each pending payout and updates DB confirm_hash with tranaction ID of commiteth bot account's confirmation." [] (log/info "In update-confirm-hash") (p :update-confirm-hash - (doseq [{issue-id :issue_id - execute-hash :execute_hash} (db-bounties/pending-payouts)] + (doseq [{:keys [issue-id execute-hash]} (db-bounties/pending-payouts)] (log/info "pending payout:" execute-hash) (when-let [receipt (eth/get-transaction-receipt execute-hash)] (log/info "execution receipt for issue #" issue-id ": " receipt) @@ -162,8 +120,7 @@ "Sets watch-hash to NULL for bounties where watch tx has been mined. Used to avoid unneeded watch transactions in update-bounty-token-balances" [] (p :update-watch-hash - (doseq [{issue-id :issue_id - watch-hash :watch_hash} (db-bounties/pending-watch-calls)] + (doseq [{:keys [issue-id watch-hash]} (db-bounties/pending-watch-calls)] (log/info "pending watch call" watch-hash) (when-let [receipt (eth/get-transaction-receipt watch-hash)] (db-bounties/update-watch-hash issue-id nil))))) @@ -182,18 +139,8 @@ [] (log/info "In update-payout-receipt") (p :update-payout-receipt - (doseq [{issue-id :issue_id - payout-hash :payout_hash - contract-address :contract_address - repo :repo - owner :owner - comment-id :comment_id - issue-number :issue_number - balance-eth :balance_eth - tokens :tokens - confirm-id :confirm_hash - payee-login :payee_login - updated :updated} (db-bounties/confirmed-payouts)] + (doseq [{:keys [payout-hash contract-address confirm-hash issue-id updated] :as issue} + (db-bounties/confirmed-payouts)] (log/debug "confirmed payout:" payout-hash) (try (if-let [receipt (eth/get-transaction-receipt payout-hash)] @@ -204,21 +151,14 @@ (> contract-eth-balance 0)) (do (log/info "Contract still has funds") - (when (multisig/is-confirmed? contract-address confirm-id) + (when (multisig/is-confirmed? contract-address confirm-hash) (log/info "Detected bounty with funds and confirmed payout, calling executeTransaction") - (let [execute-tx-hash (multisig/execute-tx contract-address confirm-id)] + (let [execute-tx-hash (multisig/execute-tx contract-address confirm-hash)] (log/info "execute tx:" execute-tx-hash)))) (do (log/info "Payout has succeeded, saving payout receipt for issue #" issue-id ": " receipt) - (db-bounties/update-payout-receipt issue-id receipt) - (github/update-paid-issue-comment owner - repo - comment-id - contract-address - (eth-decimal->str balance-eth) - tokens - payee-login)))) + (github/update-comment (assoc issue :payout-receipt receipt))))) (when (older-than-3h? updated) (log/info "Resetting payout hash for issue" issue-id "as it has not been mined in 3h") (db-bounties/reset-payout-hash issue-id))) @@ -265,39 +205,11 @@ [] (log/info "In update-contract-internal-balances") (p :update-contract-internal-balances - (doseq [{issue-id :issue_id - bounty-address :contract_address - watch-hash :watch_hash} + (doseq [{:keys [issue-id bounty-address watch-hash]} (db-bounties/open-bounty-contracts)] (update-bounty-token-balances issue-id bounty-address watch-hash))) (log/info "Exit update-contract-internal-balances")) -(defn get-bounty-funds - "Get funds in given bounty contract. - Returns map of asset -> balance - + key total-usd -> current total USD value for all funds" - [bounty-addr] - (let [token-balances (multisig/token-balances bounty-addr) - eth-balance (read-string (eth/get-balance-eth bounty-addr 6)) - all-funds - (merge token-balances - {:ETH eth-balance})] - (merge all-funds {:total-usd (fiat-util/bounty-usd-value all-funds)}))) - - -(defn update-issue-usd-value - [bounty-addr] - (let [funds (get-bounty-funds bounty-addr)] - (issues/update-usd-value bounty-addr - (:total-usd funds)))) - -(defn update-open-issue-usd-values - "Sum up current USD values of all crypto assets in a bounty and store to DB" - [] - (p :update-open-issue-usd-values - (doseq [{bounty-addr :contract_address} - (db-bounties/open-bounty-contracts)] - (update-issue-usd-value bounty-addr)))) (defn float= ([x y] (float= x y 0.0000001)) @@ -314,55 +226,38 @@ [] (log/info "In update-balances") (p :update-balances - (doseq [{contract-address :contract_address - owner :owner - repo :repo - comment-id :comment_id - issue-id :issue_id - db-balance-eth :balance_eth - db-tokens :tokens - issue-number :issue_number} (db-bounties/open-bounty-contracts)] - (try - (when comment-id - (let [balance-eth-str (eth/get-balance-eth contract-address 6) - balance-eth (read-string balance-eth-str) - token-balances (multisig/token-balances contract-address)] - (log/debug "update-balances" balance-eth - balance-eth-str token-balances owner repo issue-number) + (doseq [{:keys [contract-address owner + repo balance-eth tokens + issue-number + comment-id] :as issue} + (db-bounties/open-bounty-contracts)] + (try + (when comment-id + (let [balance-eth-str (eth/get-balance-eth contract-address 6) + current-balance-eth (read-string balance-eth-str) + token-balances (multisig/token-balances contract-address)] + (log/debug "update-balances" balance-eth + balance-eth-str token-balances owner repo issue-number) - (when (or - (not (float= db-balance-eth balance-eth)) - (not (map-float= db-tokens token-balances))) - (log/info "balances differ") - (log/info "ETH (db):" db-balance-eth (type db-balance-eth) ) - (log/info "ETH (chain):" balance-eth (type balance-eth) ) - (log/info "ETH cmp:" (float= db-balance-eth balance-eth)) - (log/info "tokens (db):" db-tokens (type db-tokens) (type (:SNT db-tokens))) - (log/info "tokens (chain):" token-balances (type token-balances) (type (:SNT token-balances))) - (log/debug "tokens cmp:" (= db-tokens token-balances)) + (when (or + (not (float= current-balance-eth balance-eth)) + (not (map-float= tokens token-balances))) + (log/info "balances differ") + (log/info "ETH (db):" balance-eth (type balance-eth) ) + (log/info "ETH (chain):" current-balance-eth (type current-balance-eth) ) + (log/info "ETH cmp:" (float= balance-eth current-balance-eth)) + (log/info "tokens (db):" tokens (type tokens) (type (:SNT tokens))) + (log/info "tokens (chain):" token-balances (type token-balances) (type (:SNT token-balances))) + (log/debug "tokens cmp:" (= tokens token-balances)) - (issues/update-eth-balance contract-address balance-eth) - (issues/update-token-balances contract-address token-balances) - (bounties/update-bounty-comment-image issue-id - owner - repo - issue-number - contract-address - balance-eth - balance-eth-str - token-balances) - (github/update-comment owner - repo - comment-id - issue-number - contract-address - balance-eth - balance-eth-str - token-balances) - (update-issue-usd-value contract-address)))) - (catch Throwable ex - (do (log/error "update-balances exception:" ex) - (clojure.stacktrace/print-stack-trace ex)))))) + (issues/update-balances contract-address + current-balance-eth token-balances + (fiat-util/bounty-usd-value + (merge token-balances {:ETH current-balance-eth}))) + (github/update-comment issue)))) + (catch Throwable ex + (do (log/error "update-balances exception:" ex) + (clojure.stacktrace/print-stack-trace ex)))))) (log/info "Exit update-balances")) @@ -398,8 +293,7 @@ (log/info "run-10-min-interval-tasks" time) (run-tasks [update-contract-internal-balances - update-balances - update-open-issue-usd-values]) + update-balances]) (log/info "run-10-min-interval-tasks done"))) diff --git a/src/clj/commiteth/util/png_rendering.clj b/src/clj/commiteth/util/png_rendering.clj index 997ad5a..1bd31aa 100644 --- a/src/clj/commiteth/util/png_rendering.clj +++ b/src/clj/commiteth/util/png_rendering.clj @@ -1,7 +1,6 @@ (ns commiteth.util.png-rendering (:require [commiteth.layout :refer [render]] [commiteth.config :refer [env]] - [commiteth.github.core :as github] [commiteth.db.comment-images :as db] [commiteth.db.bounties :as db-bounties] [clj.qrgen :as qr] @@ -58,21 +57,6 @@ nil)))) -(defn export-comment-image - "Retrieve image PNG from DB and write to file" - [owner repo issue-number filename] - (let [{owner :owner - repo :repo - issue-id :issue_id - balance-eth :balance_eth} (db-bounties/get-bounty owner repo issue-number) - hash (github/github-comment-hash - owner - repo - issue-number - balance-eth)] - (with-open [w (io/output-stream filename)] - (.write w (:png_data (db/get-image-data issue-id hash)))))) - (comment (with-open [w (io/output-stream "foo.png")] diff --git a/src/clj/commiteth/util/util.clj b/src/clj/commiteth/util/util.clj index f85211e..d95cdec 100644 --- a/src/clj/commiteth/util/util.clj +++ b/src/clj/commiteth/util/util.clj @@ -1,6 +1,7 @@ (ns commiteth.util.util (:require [clj-http.client :as http] + [clojure.string :as str] [clojure.data.json :as json])) @@ -14,3 +15,10 @@ (->> (http/get url) (:body) (json/read-str))) + +(defmacro to-map [& vars] + (into {} (map #(vector (keyword %1) %1) vars))) + +(defmacro to-db-map [& vars] + (into {} (map #(vector (keyword (str/replace (name %1) "-" "_")) %1) vars))) + From 3d408fcfbef376148aaffaf77d696df39b92760b Mon Sep 17 00:00:00 2001 From: Vitaliy Vlasov Date: Thu, 19 Apr 2018 16:31:04 +0300 Subject: [PATCH 02/85] Fix merge errors --- src/clj/commiteth/bounties.clj | 3 +-- src/clj/commiteth/scheduler.clj | 19 +++++-------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/clj/commiteth/bounties.clj b/src/clj/commiteth/bounties.clj index 95ab1e8..9a76597 100644 --- a/src/clj/commiteth/bounties.clj +++ b/src/clj/commiteth/bounties.clj @@ -29,7 +29,6 @@ (do (log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id transaction-hash) (github/update-comment (to-map owner repo issue-number transaction-hash)) -(log/infof "issue %s: post-deploying-comment response: %s" issue-id resp) (issues/update-transaction-hash issue-id transaction-hash)) (log/errorf "issue %s Failed to deploy contract to %s" issue-id owner-address)) (catch Exception ex (log/errorf ex "issue %s: deploy-contract exception" issue-id))))) @@ -41,7 +40,7 @@ created-issue (issues/create repo-id issue-id issue-number issue-title) {:keys [address owner]} (users/get-repo-owner repo-id)] (log/debug "issue %s: Adding bounty for issue %s/%s - owner address: %s" - issue-id repo issue-number owner-address) + issue-id repo issue-number address) (if (= 1 created-issue) (deploy-contract owner address repo issue-id issue-number) (log/debug "issue %s: Issue already exists in DB, ignoring")))) diff --git a/src/clj/commiteth/scheduler.clj b/src/clj/commiteth/scheduler.clj index c38d613..0dded33 100644 --- a/src/clj/commiteth/scheduler.clj +++ b/src/clj/commiteth/scheduler.clj @@ -45,7 +45,6 @@ [] (log/info "In update-issue-contract-address") (p :update-issue-contract-address -<<<<<<< HEAD (doseq [{:keys [issue-id transaction-hash]} (issues/list-pending-deployments)] (log/infof "issue %s: pending deployment: %s" issue-id transaction-hash) (try @@ -76,17 +75,9 @@ (defn deploy-pending-contracts "Under high-concurrency circumstances or in case geth is in defunct state, a bounty contract may not deploy successfully when the bounty label is addded to an issue. This function deploys such contracts." [] - (p :deploy-pending-contracts - (doseq [{:keys [issue-id issue-number owner owner-address repo]} - (db-bounties/pending-contracts)] - (log/infof "issue %s: Trying to re-deploy failed bounty contract deployment" issue-id) - (try - (bounties/deploy-contract owner owner-address repo issue-id issue-number) - (doseq [{issue-id :issue_id - issue-number :issue_number - owner :owner - owner-address :owner_address - repo :repo} (db-bounties/pending-contracts)] + (p :deploy-pending-contracts + (doseq [{:keys [issue-id issue-number owner owner-address repo]} + (db-bounties/pending-contracts)] (log/infof "issue %s: Trying to re-deploy failed bounty contract deployment" issue-id) (try (bounties/deploy-contract owner owner-address repo issue-id issue-number) @@ -98,7 +89,6 @@ [] (log/info "In self-sign-bounty") (p :self-sign-bounty -<<<<<<< HEAD (doseq [{:keys [contract-address winner-address issue-id winner-login] :as issue} (db-bounties/pending-bounties)] (try @@ -245,6 +235,7 @@ (p :update-balances (doseq [{:keys [contract-address owner repo balance-eth tokens + issue-id issue-number comment-id] :as issue} (db-bounties/open-bounty-contracts)] @@ -273,7 +264,7 @@ (merge token-balances {:ETH current-balance-eth}))) (github/update-comment issue)))) (catch Throwable ex - (log/error ex "issue %s: update-balances exception" issue-id))))) + (log/error ex "issue %s: update-balances exception" issue-id))))) (log/info "Exit update-balances")) From d9bb04036d8fc72c5b1edfe771e73436d0af98ec Mon Sep 17 00:00:00 2001 From: Rob Culliton Date: Tue, 1 May 2018 20:26:45 -0400 Subject: [PATCH 03/85] single click bounty revocation --- README.md | 4 +- contracts/README.md | 4 +- contracts/build.sh | 12 +- ...251-update-issue-execution-posted.down.sql | 1 + ...53251-update-issue-execution-posted.up.sql | 1 + resources/sql/queries.sql | 53 +++++++- src/clj/commiteth/db/bounties.clj | 10 ++ src/clj/commiteth/db/issues.clj | 16 +++ src/clj/commiteth/db/pull_requests.clj | 4 +- src/clj/commiteth/eth/core.clj | 14 ++ src/clj/commiteth/routes/services.clj | 69 ++++++++-- src/clj/commiteth/scheduler.clj | 128 ++++++++++++------ src/cljs/commiteth/handlers.cljs | 42 +++++- src/cljs/commiteth/manage_payouts.cljs | 20 ++- 14 files changed, 311 insertions(+), 67 deletions(-) create mode 100644 resources/migrations/20180426153251-update-issue-execution-posted.down.sql create mode 100644 resources/migrations/20180426153251-update-issue-execution-posted.up.sql diff --git a/README.md b/README.md index b0a12dd..c7260b9 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,9 @@ lein less auto ``` ### Solidity compilation -Invoke `build-contracts` Leiningen task to compile Solidity files into Java classes: +Compile Solidity files into Java classes with: ``` -lein build-contracts +cd contracts && ./build.sh ``` ### Clojure app without REPL diff --git a/contracts/README.md b/contracts/README.md index f92dc0e..59015c9 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -2,8 +2,8 @@ This directory contains all the underlying smart contracts used by the OpenBounty platform. -- A script `contracts/build.sh` is part of this repository and can be used to -compile the contracts and copy Java interfaces into `src/java/`. +-- A script `build.sh` is part of this directory and can be used to +-compile the contracts and copy Java interfaces into `src/java/`. In order to run the script the following dependencies have to be met: diff --git a/contracts/build.sh b/contracts/build.sh index 7b00d8c..99e4991 100755 --- a/contracts/build.sh +++ b/contracts/build.sh @@ -1,12 +1,18 @@ #!/bin/bash -eu -SOLC=$(which solc) -WEB3J=$(which web3j) +function print_dependency_message +{ + echo "error: " $1 "must already be installed!" +} + +SOLC=$(which solc) || print_dependency_message "solc" + +WEB3J=$(which web3j) || print_dependency_message "web3" rm -f resources/contracts/*.{abi,bin} # compile contracts -for f in contracts/{TokenReg,MultiSigTokenWallet*}.sol; do +for f in {TokenReg,MultiSigTokenWallet*}.sol; do $SOLC $f --overwrite --bin --abi --optimize -o resources/contracts done diff --git a/resources/migrations/20180426153251-update-issue-execution-posted.down.sql b/resources/migrations/20180426153251-update-issue-execution-posted.down.sql new file mode 100644 index 0000000..99410a6 --- /dev/null +++ b/resources/migrations/20180426153251-update-issue-execution-posted.down.sql @@ -0,0 +1 @@ +ALTER TABLE "public"."issues" DROP COLUMN "execute_posted"; diff --git a/resources/migrations/20180426153251-update-issue-execution-posted.up.sql b/resources/migrations/20180426153251-update-issue-execution-posted.up.sql new file mode 100644 index 0000000..2e4dd32 --- /dev/null +++ b/resources/migrations/20180426153251-update-issue-execution-posted.up.sql @@ -0,0 +1 @@ +ALTER TABLE "public"."issues" ADD COLUMN "execute_posted" boolean; diff --git a/resources/sql/queries.sql b/resources/sql/queries.sql index 0f46d62..35bca09 100644 --- a/resources/sql/queries.sql +++ b/resources/sql/queries.sql @@ -73,7 +73,6 @@ FROM repositories WHERE owner = :owner AND repo = :repo; - -- :name create-repository! :str]] [crypto.random :as random] [clojure.set :refer [rename-keys]] - [clojure.string :as str])) + [clojure.string :as str] + [commiteth.eth.multisig-wallet :as multisig] + [commiteth.db.bounties :as db-bounties] + [commiteth.scheduler :as scheduler] + [clj-time.core :as t] + [clj-time.periodic :refer [periodic-seq]])) (defn add-bounties-for-existing-issues? [] (env :add-bounties-for-existing-issues false)) @@ -155,13 +162,38 @@ (let [whitelist (env :user-whitelist #{})] (whitelist user))) +(defn execute-revocation [issue-id contract-address payout-address] + (log/info (str "executing revocation for " issue-id "at" contract-address)) + (let [execute-hash (multisig/send-all {:contract contract-address + :payout-address payout-address + :internal-tx-id (str "payout-github-issue-" issue-id)}) + execute-write (db-bounties/update-execute-hash issue-id execute-hash)] + {:execute-hash execute-hash + :execute-write execute-write})) + +(defn update-confirm-hash + [issue-id execute-hash] + (log/infof "issue %s: pending payout: %s" issue-id execute-hash) + (try + (when-let [receipt (eth/get-transaction-receipt execute-hash)] + (log/infof "issue %s: execution receipt for issue " issue-id receipt) + (when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)] + (log/infof "issue %s: confirm hash:" issue-id confirm-hash) + (db-bounties/update-confirm-hash issue-id confirm-hash) + {:confirm_hash confirm-hash})) + (catch Throwable ex + (log/errorf ex "issue %s: update-confirm-hash exception:" issue-id)))) + (defapi service-routes (when (:dev env) - {:swagger {:ui "/swagger-ui" - :spec "/swagger.json" - :data {:info {:version "0.1" - :title "commitETH API" - :description "commitETH API"}}}}) + {:swagger {:ui "/swagger-ui" + :spec "/swagger.json" + :data {:info {:version "0.1" + :title "commitETH API" + :description "commitETH API"}}} + :exceptions {:handlers + {::ex/request-parsing (ex/with-logging ex/request-parsing-handler :info) + ::ex/response-validation (ex/with-logging ex/response-validation-handler :error)}}}) (context "/api" [] (GET "/top-hunters" [] @@ -191,11 +223,11 @@ (POST "/" [] :auth-rules authenticated? :current-user user - :body [body {:address s/Str + :body [body {:address s/Str :is_hidden_in_hunters s/Bool}] :summary "Updates user's fields." - (let [user-id (:id user) + (let [user-id (:id user) {:keys [address]} body] (when-not (eth/valid-address? address) @@ -226,9 +258,9 @@ (log/debug "/bounty/X/payout" params) (let [{issue :issue payout-hash :payout-hash} params - result (bounties-db/update-payout-hash - (Integer/parseInt issue) - payout-hash)] + result (bounties-db/update-payout-hash + (Integer/parseInt issue) + payout-hash)] (log/debug "result" result) (if (= 1 result) (ok) @@ -237,4 +269,17 @@ :auth-rules authenticated? :current-user user (log/debug "/user/bounties") - (ok (user-bounties user)))))) + (ok (user-bounties user))) + (POST "/revoke" {{issue-id :issue-id + contract-address :contract-address + owner-address :owner-address} :params} + :auth-rules authenticated? + :current-user user + (do (log/infof "calling revoke-initiate for %s with %s %s" issue-id contract-address owner-address) + (if-let [{:keys [execute-hash execute-write]} (execute-revocation issue-id contract-address owner-address)] + (if (scheduler/poll-transaction-logs execute-hash contract-address) + (if-let [{confirm-hash :confirm_hash} (update-confirm-hash issue-id execute-hash)] + (ok {:confirm-hash confirm-hash}) + (bad-request "The confirm hash could not be updated")) + (bad-request "The transaction hash could not be confirmed in a reasonable amount of time")) + (bad-request (str "We were unable to withdraw everything from " contract-address)))))))) diff --git a/src/clj/commiteth/scheduler.clj b/src/clj/commiteth/scheduler.clj index 57aa61e..aefff23 100644 --- a/src/clj/commiteth/scheduler.clj +++ b/src/clj/commiteth/scheduler.clj @@ -149,25 +149,6 @@ (log/error ex "issue %s: self-sign-bounty exception" issue-id))))) (log/info "Exit self-sign-bounty")) -(defn update-confirm-hash - "Gets transaction receipt for each pending payout and updates DB confirm_hash with tranaction ID of commiteth bot account's confirmation." - [] - (log/info "In update-confirm-hash") - (p :update-confirm-hash - (doseq [{issue-id :issue_id - execute-hash :execute_hash} (db-bounties/pending-payouts)] - (log/infof "issue %s: pending payout: %s" issue-id execute-hash) - (try - (when-let [receipt (eth/get-transaction-receipt execute-hash)] - (log/infof "issue %s: execution receipt for issue " issue-id receipt) - (when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)] - (log/infof "issue %s: confirm hash:" issue-id confirm-hash) - (db-bounties/update-confirm-hash issue-id confirm-hash))) - (catch Throwable ex - (log/errorf ex "issue %s: update-confirm-hash exception:" issue-id))))) - (log/info "Exit update-confirm-hash")) - - (defn update-watch-hash "Sets watch-hash to NULL for bounties where watch tx has been mined. Used to avoid unneeded watch transactions in update-bounty-token-balances" [] @@ -190,24 +171,8 @@ (println "hour diff:" diff) (> diff 3))) -(defn update-payout-receipt - "Gets transaction receipt for each confirmed payout and updates payout_hash" - [] - (log/info "In update-payout-receipt") - (p :update-payout-receipt - (doseq [{issue-id :issue_id - payout-hash :payout_hash - contract-address :contract_address - repo :repo - owner :owner - comment-id :comment_id - issue-number :issue_number - balance-eth :balance_eth - tokens :tokens - confirm-id :confirm_hash - payee-login :payee_login - updated :updated} (db-bounties/confirmed-payouts)] - (log/infof "issue %s: confirmed payout: %s" issue-id payout-hash) +(defn update-payout-receipt [owner repo comment-id balance-eth tokens payee-login issue-id confirm-hash payout-hash contract-address updated] + (log/infof "issue %s: confirmed payout: %s" issue-id payout-hash) (try (if-let [receipt (eth/get-transaction-receipt payout-hash)] (let [contract-tokens (multisig/token-balances contract-address) @@ -217,9 +182,9 @@ (> contract-eth-balance 0)) (do (log/infof "issue %s: Contract (%s) still has funds" issue-id contract-address) - (when (multisig/is-confirmed? contract-address confirm-id) + (when (multisig/is-confirmed? contract-address confirm-hash) (log/infof "issue %s: Detected bounty with funds and confirmed payout, calling executeTransaction" issue-id) - (let [execute-tx-hash (multisig/execute-tx contract-address confirm-id)] + (let [execute-tx-hash (multisig/execute-tx contract-address confirm-hash)] (log/infof "issue %s: execute tx: %s" issue-id execute-tx-hash)))) (do @@ -236,8 +201,47 @@ (log/warn "issue %s: Resetting payout hash for issue as it has not been mined in 3h" issue-id) (db-bounties/reset-payout-hash issue-id))) (catch Throwable ex - (log/error ex "issue %s: update-payout-receipt exception" issue-id))))) - (log/info "Exit update-payout-receipt")) + (log/error ex "issue %s: update-payout-receipt exception" issue-id)))) + +(defn update-revoked-payout-receipts + "Gets transaction receipt for each confirmed revocation and updates payout_hash" + [] + (log/info "In update-revoked-payout-receipts") + (p :update-payout-receipts + (doseq [{issue-id :issue_id + payout-hash :payout_hash + contract-address :contract_address + repo :repo + owner :owner + comment-id :comment_id + issue-number :issue_number + balance-eth :balance_eth + tokens :tokens + confirm-hash :confirm_hash + payee-login :payee_login + updated :updated} (db-bounties/confirmed-revocation-payouts)] + (update-payout-receipt owner repo comment-id balance-eth tokens payee-login issue-id confirm-hash payout-hash contract-address updated))) + (log/info "Exit update-revoked-payout-receipts")) + +(defn update-payout-receipts + "Gets transaction receipt for each confirmed payout and updates payout_hash" + [] + (log/info "In update-payout-receipts") + (p :update-payout-receipts + (doseq [{issue-id :issue_id + payout-hash :payout_hash + contract-address :contract_address + repo :repo + owner :owner + comment-id :comment_id + issue-number :issue_number + balance-eth :balance_eth + tokens :tokens + confirm-hash :confirm_hash + payee-login :payee_login + updated :updated} (db-bounties/confirmed-payouts)] + (update-payout-receipt owner repo comment-id balance-eth tokens payee-login issue-id confirm-hash payout-hash contract-address updated))) + (log/info "Exit update-payout-receipts")) (defn abs "(abs n) is the absolute value of n" @@ -396,8 +400,8 @@ (run-tasks [deploy-pending-contracts update-issue-contract-address - update-confirm-hash - update-payout-receipt + update-payout-receipts + update-revoked-payout-receipts update-watch-hash self-sign-bounty ]) @@ -436,3 +440,41 @@ :stop (do (log/info "stopping scheduler") (scheduler))) + +(defn contract-confirmation-logs [contract-address] + "retrives all log events for the confirmation topic since contract creation" + (some-> contract-address + issues/get-issue-by-contract-address + :transaction_hash + eth/get-transaction-by-hash + :blockNumber + (eth/get-logs contract-address [(:confirmation multisig/topics)]))) + +(defn hash-in-logs? + "return true if the transaction hash is present in the queryable blockchain" + [hash logs] + (some #(= hash (:transactionHash %)) logs)) + +(defn execution-status [execute-hash contract-address] + "check to see if a given execute-hash has been confirmed" + (log/infof "checking contract for logs containing %s" execute-hash) + (let [logs (contract-confirmation-logs contract-address)] + (hash-in-logs? execute-hash logs))) + + +(defn poll-transaction-logs [execute-hash contract-address] + "check for execution hash in logs for a few minutes" + (let [found? (promise) + intervals (take 6 + (periodic-seq (t/now) + (t/seconds 30)))] + ;; polling will be slow but if we want to move to an event driven + ;; model then we can listen for events, rather than logs, once we're + ;; using a geth node again + (chime-at intervals + (fn [time] + (when (execution-status execute-hash contract-address) + (deliver found? true))) + {:on-finished (fn [] + (deliver found? false))}) + @found?)) diff --git a/src/cljs/commiteth/handlers.cljs b/src/cljs/commiteth/handlers.cljs index 63b3fda..4f43b0d 100644 --- a/src/cljs/commiteth/handlers.cljs +++ b/src/cljs/commiteth/handlers.cljs @@ -1,6 +1,7 @@ (ns commiteth.handlers (:require [commiteth.db :as db] - [re-frame.core :refer [dispatch + [re-frame.core :refer [debug + dispatch reg-event-db reg-event-fx reg-fx @@ -20,6 +21,9 @@ (rf-storage/reg-co-fx! :commiteth-sob {:fx :store :cofx :store}) +;; https://github.com/Day8/re-frame/blob/master/docs/Debugging-Event-Handlers.md +(def interceptors [(when ^boolean goog.DEBUG debug)]) + (reg-fx :http (fn [{:keys [method url on-success on-error finally params]}] @@ -64,6 +68,7 @@ (reg-event-fx :initialize-web3 + interceptors (fn [{:keys [db]} [_]] (let [injected-web3 (common/web3) w3 (when (boolean injected-web3) @@ -209,6 +214,7 @@ (reg-event-fx :load-owner-bounties + interceptors (fn [{:keys [db]} [_]] {:db (assoc db :owner-bounties-loading? true) :http {:method GET @@ -376,6 +382,7 @@ (reg-event-fx :save-payout-hash + interceptors (fn [{:keys [db]} [_ issue-id payout-hash]] {:db db :http {:method POST @@ -405,8 +412,40 @@ (defn strip-0x [x] (str/replace x #"^0x" "")) +(reg-event-fx + :revoke-bounty-success + interceptors + (fn [{:keys [db]} [_ {:keys [issue-id owner-address contract-address confirm-hash]}]] + {:dispatch [:confirm-payout {:issue_id issue-id + :owner_address owner-address + :contract_address contract-address + :confirm_hash confirm-hash}]})) + +(reg-event-fx + :revoke-bounty-error + interceptors + (fn [{:keys [db]} [_ issue-id response]] + {:dispatch [:set-flash-message + :error (if (= 400 (:status response)) + (:response response) + (str "Failed to initiate revocation for: " issue-id + (:status-text response)))]})) + +(reg-event-fx + :revoke-bounty + interceptors + (fn [{:keys [db]} [_ fields]] + (let [issue-id (:issue_id fields)] + {:http {:method POST + :url "/api/user/revoke" + ;; merge returned confirm hash with + :on-success #(dispatch [:revoke-bounty-success (merge fields %)]) + :on-error #(dispatch [:revoke-bounty-error issue-id %]) + :params fields}}))) + (reg-event-fx :confirm-payout + interceptors (fn [{:keys [db]} [_ {issue-id :issue_id owner-address :owner_address contract-address :contract_address @@ -437,6 +476,7 @@ (reg-event-fx :payout-confirmed + interceptors (fn [{:keys [db]} [_ issue-id]] {:dispatch [:load-owner-bounties] :db (-> db diff --git a/src/cljs/commiteth/manage_payouts.cljs b/src/cljs/commiteth/manage_payouts.cljs index c2f0f44..f730cba 100644 --- a/src/cljs/commiteth/manage_payouts.cljs +++ b/src/cljs/commiteth/manage_payouts.cljs @@ -6,6 +6,7 @@ [commiteth.routes :as routes] [commiteth.model.bounty :as bnt] [commiteth.ui.balances :as ui-balances] + [commiteth.config :as config] [commiteth.common :as common :refer [human-time]])) (defn pr-url [{owner :repo_owner @@ -13,6 +14,12 @@ repo :repo_name}] (str "https://github.com/" owner "/" repo "/pull/" pr-number)) +;; TODO put this in cljc file +(defn etherscan-tx-url [tx-id] + (str "https://" + (when (config/on-testnet?) "ropsten.") + "etherscan.io/tx/" tx-id)) + (def primary-button-button :button.f7.ttu.tracked.outline-0.bg-sob-blue.white.pv3.ph4.pg-med.br3.bn.pointer.shadow-7) (def primary-button-link :a.dib.tc.f7.ttu.tracked.bg-sob-blue.white.pv2.ph3.pg-med.br2.pointer.hover-white.shadow-7) @@ -57,6 +64,15 @@ "Signed off" "Confirm Payment")]))) +(defn revoke-button [{issue-id :issue-id + contract-address :contract_address + owner-address :owner_address}] + [:button.ui.button + {:on-click #(rf/dispatch [:revoke-bounty {:contract-address contract-address + :issue-id issue-id + :owner-address owner-address}])} + "Revoke"]) + (defn confirm-row [bounty claim] (let [payout-address-available? (:payout_address bounty)] [:div @@ -236,10 +252,12 @@ [:div [ui-balances/usd-value-label (:value-usd bounty)]]]) -(defn unclaimed-bounty [bounty] +(defn unclaimed-bounty [{value-usd :value-usd :as bounty}] [:div.w-third-l.fl-l.pa2 [square-card [bounty-title-link bounty {:show-date? true :max-length 60}] + (when (> value-usd 0) + [revoke-button bounty]) [small-card-balances bounty]]]) (defn paid-bounty [bounty] From cec0718b1ec2b698f9f0ac683730ef9ace34dc96 Mon Sep 17 00:00:00 2001 From: Rob Culliton Date: Tue, 1 May 2018 21:53:02 -0400 Subject: [PATCH 04/85] move update-confirm-hash back to scheduler, make sure job is in 1-min tasks --- src/clj/commiteth/routes/services.clj | 14 +--- src/clj/commiteth/scheduler.clj | 99 +++++++++++++++++---------- 2 files changed, 63 insertions(+), 50 deletions(-) diff --git a/src/clj/commiteth/routes/services.clj b/src/clj/commiteth/routes/services.clj index 3808e2d..9f37dcc 100644 --- a/src/clj/commiteth/routes/services.clj +++ b/src/clj/commiteth/routes/services.clj @@ -171,18 +171,6 @@ {:execute-hash execute-hash :execute-write execute-write})) -(defn update-confirm-hash - [issue-id execute-hash] - (log/infof "issue %s: pending payout: %s" issue-id execute-hash) - (try - (when-let [receipt (eth/get-transaction-receipt execute-hash)] - (log/infof "issue %s: execution receipt for issue " issue-id receipt) - (when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)] - (log/infof "issue %s: confirm hash:" issue-id confirm-hash) - (db-bounties/update-confirm-hash issue-id confirm-hash) - {:confirm_hash confirm-hash})) - (catch Throwable ex - (log/errorf ex "issue %s: update-confirm-hash exception:" issue-id)))) (defapi service-routes (when (:dev env) @@ -278,7 +266,7 @@ (do (log/infof "calling revoke-initiate for %s with %s %s" issue-id contract-address owner-address) (if-let [{:keys [execute-hash execute-write]} (execute-revocation issue-id contract-address owner-address)] (if (scheduler/poll-transaction-logs execute-hash contract-address) - (if-let [{confirm-hash :confirm_hash} (update-confirm-hash issue-id execute-hash)] + (if-let [{confirm-hash :confirm_hash} (scheduler/update-confirm-hash issue-id execute-hash)] (ok {:confirm-hash confirm-hash}) (bad-request "The confirm hash could not be updated")) (bad-request "The transaction hash could not be confirmed in a reasonable amount of time")) diff --git a/src/clj/commiteth/scheduler.clj b/src/clj/commiteth/scheduler.clj index aefff23..27706a9 100644 --- a/src/clj/commiteth/scheduler.clj +++ b/src/clj/commiteth/scheduler.clj @@ -149,6 +149,30 @@ (log/error ex "issue %s: self-sign-bounty exception" issue-id))))) (log/info "Exit self-sign-bounty")) +(defn update-confirm-hash + [issue-id execute-hash] + (log/infof "issue %s: pending payout: %s" issue-id execute-hash) + (try + (when-let [receipt (eth/get-transaction-receipt execute-hash)] + (log/infof "issue %s: execution receipt for issue " issue-id receipt) + (when-let [confirm-hash (multisig/find-confirmation-tx-id receipt)] + (log/infof "issue %s: confirm hash:" issue-id confirm-hash) + (db-bounties/update-confirm-hash issue-id confirm-hash) + {:confirm_hash confirm-hash})) + (catch Throwable ex + (log/errorf ex "issue %s: update-confirm-hash exception:" issue-id)))) + +(defn update-confirm-hashes + "Gets transaction receipt for each pending payout and updates DB confirm_hash with tranaction ID of commiteth bot account's confirmation." + [] + (log/info "In update-confirm-hashes") + (p :update-confirm-hash + (doseq [{issue-id :issue_id + execute-hash :execute_hash} (db-bounties/pending-payouts)] + + (update-confirm-hash issue-id execute-hash))) + (log/info "Exit update-confirm-hash")) + (defn update-watch-hash "Sets watch-hash to NULL for bounties where watch tx has been mined. Used to avoid unneeded watch transactions in update-bounty-token-balances" [] @@ -252,6 +276,43 @@ (neg? n) (- n) :else n)) +(defn contract-confirmation-logs [contract-address] + "retrives all log events for the confirmation topic since contract creation" + (some-> contract-address + issues/get-issue-by-contract-address + :transaction_hash + eth/get-transaction-by-hash + :blockNumber + (eth/get-logs contract-address [(:confirmation multisig/topics)]))) + +(defn hash-in-logs? + "return true if the transaction hash is present in the queryable blockchain" + [hash logs] + (some #(= hash (:transactionHash %)) logs)) + +(defn execution-status [execute-hash contract-address] + "check to see if a given execute-hash has been confirmed" + (log/infof "checking contract for logs containing %s" execute-hash) + (let [logs (contract-confirmation-logs contract-address)] + (hash-in-logs? execute-hash logs))) + + +(defn poll-transaction-logs [execute-hash contract-address] + "check for execution hash in logs for a few minutes" + (let [found? (promise) + intervals (take 6 + (periodic-seq (t/now) + (t/seconds 30)))] + ;; polling will be slow but if we want to move to an event driven + ;; model then we can listen for events, rather than logs, once we're + ;; using a geth node again + (chime-at intervals + (fn [time] + (when (execution-status execute-hash contract-address) + (deliver found? true))) + {:on-finished (fn [] + (deliver found? false))}) + @found?)) (defn update-bounty-token-balances "Helper function for updating internal ERC20 token balances to token @@ -400,6 +461,7 @@ (run-tasks [deploy-pending-contracts update-issue-contract-address + update-confirm-hashes update-payout-receipts update-revoked-payout-receipts update-watch-hash @@ -441,40 +503,3 @@ (log/info "stopping scheduler") (scheduler))) -(defn contract-confirmation-logs [contract-address] - "retrives all log events for the confirmation topic since contract creation" - (some-> contract-address - issues/get-issue-by-contract-address - :transaction_hash - eth/get-transaction-by-hash - :blockNumber - (eth/get-logs contract-address [(:confirmation multisig/topics)]))) - -(defn hash-in-logs? - "return true if the transaction hash is present in the queryable blockchain" - [hash logs] - (some #(= hash (:transactionHash %)) logs)) - -(defn execution-status [execute-hash contract-address] - "check to see if a given execute-hash has been confirmed" - (log/infof "checking contract for logs containing %s" execute-hash) - (let [logs (contract-confirmation-logs contract-address)] - (hash-in-logs? execute-hash logs))) - - -(defn poll-transaction-logs [execute-hash contract-address] - "check for execution hash in logs for a few minutes" - (let [found? (promise) - intervals (take 6 - (periodic-seq (t/now) - (t/seconds 30)))] - ;; polling will be slow but if we want to move to an event driven - ;; model then we can listen for events, rather than logs, once we're - ;; using a geth node again - (chime-at intervals - (fn [time] - (when (execution-status execute-hash contract-address) - (deliver found? true))) - {:on-finished (fn [] - (deliver found? false))}) - @found?)) From 56e54e10a4e5954296424e401aa6793c324e8d59 Mon Sep 17 00:00:00 2001 From: Rob Culliton Date: Tue, 1 May 2018 21:59:00 -0400 Subject: [PATCH 05/85] fix logging, error message, inline comments --- src/clj/commiteth/routes/services.clj | 2 +- src/clj/commiteth/scheduler.clj | 3 +-- src/cljs/commiteth/handlers.cljs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/clj/commiteth/routes/services.clj b/src/clj/commiteth/routes/services.clj index 9f37dcc..797d4e9 100644 --- a/src/clj/commiteth/routes/services.clj +++ b/src/clj/commiteth/routes/services.clj @@ -270,4 +270,4 @@ (ok {:confirm-hash confirm-hash}) (bad-request "The confirm hash could not be updated")) (bad-request "The transaction hash could not be confirmed in a reasonable amount of time")) - (bad-request (str "We were unable to withdraw everything from " contract-address)))))))) + (bad-request (str "Unable to withdraw everything from " contract-address)))))))) diff --git a/src/clj/commiteth/scheduler.clj b/src/clj/commiteth/scheduler.clj index 27706a9..78838a3 100644 --- a/src/clj/commiteth/scheduler.clj +++ b/src/clj/commiteth/scheduler.clj @@ -171,7 +171,7 @@ execute-hash :execute_hash} (db-bounties/pending-payouts)] (update-confirm-hash issue-id execute-hash))) - (log/info "Exit update-confirm-hash")) + (log/info "Exit update-confirm-hashes")) (defn update-watch-hash "Sets watch-hash to NULL for bounties where watch tx has been mined. Used to avoid unneeded watch transactions in update-bounty-token-balances" @@ -502,4 +502,3 @@ :stop (do (log/info "stopping scheduler") (scheduler))) - diff --git a/src/cljs/commiteth/handlers.cljs b/src/cljs/commiteth/handlers.cljs index 4f43b0d..fdc5a5b 100644 --- a/src/cljs/commiteth/handlers.cljs +++ b/src/cljs/commiteth/handlers.cljs @@ -438,7 +438,6 @@ (let [issue-id (:issue_id fields)] {:http {:method POST :url "/api/user/revoke" - ;; merge returned confirm hash with :on-success #(dispatch [:revoke-bounty-success (merge fields %)]) :on-error #(dispatch [:revoke-bounty-error issue-id %]) :params fields}}))) From e10c8b577c2258dc4a2d33411ce50fcf5491545b Mon Sep 17 00:00:00 2001 From: Rob Culliton Date: Tue, 1 May 2018 22:12:28 -0400 Subject: [PATCH 06/85] swap ordering of revoked-payout-receipts and payout-receipts --- src/clj/commiteth/scheduler.clj | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/clj/commiteth/scheduler.clj b/src/clj/commiteth/scheduler.clj index 78838a3..fd8a069 100644 --- a/src/clj/commiteth/scheduler.clj +++ b/src/clj/commiteth/scheduler.clj @@ -227,26 +227,6 @@ (catch Throwable ex (log/error ex "issue %s: update-payout-receipt exception" issue-id)))) -(defn update-revoked-payout-receipts - "Gets transaction receipt for each confirmed revocation and updates payout_hash" - [] - (log/info "In update-revoked-payout-receipts") - (p :update-payout-receipts - (doseq [{issue-id :issue_id - payout-hash :payout_hash - contract-address :contract_address - repo :repo - owner :owner - comment-id :comment_id - issue-number :issue_number - balance-eth :balance_eth - tokens :tokens - confirm-hash :confirm_hash - payee-login :payee_login - updated :updated} (db-bounties/confirmed-revocation-payouts)] - (update-payout-receipt owner repo comment-id balance-eth tokens payee-login issue-id confirm-hash payout-hash contract-address updated))) - (log/info "Exit update-revoked-payout-receipts")) - (defn update-payout-receipts "Gets transaction receipt for each confirmed payout and updates payout_hash" [] @@ -267,6 +247,26 @@ (update-payout-receipt owner repo comment-id balance-eth tokens payee-login issue-id confirm-hash payout-hash contract-address updated))) (log/info "Exit update-payout-receipts")) +(defn update-revoked-payout-receipts + "Gets transaction receipt for each confirmed revocation and updates payout_hash" + [] + (log/info "In update-revoked-payout-receipts") + (p :update-payout-receipts + (doseq [{issue-id :issue_id + payout-hash :payout_hash + contract-address :contract_address + repo :repo + owner :owner + comment-id :comment_id + issue-number :issue_number + balance-eth :balance_eth + tokens :tokens + confirm-hash :confirm_hash + payee-login :payee_login + updated :updated} (db-bounties/confirmed-revocation-payouts)] + (update-payout-receipt owner repo comment-id balance-eth tokens payee-login issue-id confirm-hash payout-hash contract-address updated))) + (log/info "Exit update-revoked-payout-receipts")) + (defn abs "(abs n) is the absolute value of n" [n] From 63fd5c336cb3deb5934c167c176200bbcf98678c Mon Sep 17 00:00:00 2001 From: Rob Culliton Date: Wed, 2 May 2018 18:23:39 -0400 Subject: [PATCH 07/85] basic material look for 3 dot button --- resources/templates/home.html | 3 +++ src/cljs/commiteth/manage_payouts.cljs | 16 +++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/resources/templates/home.html b/resources/templates/home.html index 6afe0ca..1bbc98a 100644 --- a/resources/templates/home.html +++ b/resources/templates/home.html @@ -66,6 +66,9 @@ height="0" width="0" style="display:none;visibility:hidden"> /> + + +