Add bounties/transition fn;

add ParallelTxTracker impl
This commit is contained in:
Vitaliy Vlasov 2018-05-14 21:28:39 +03:00
parent d9db6b442f
commit 6b5790fde7
No known key found for this signature in database
GPG Key ID: A7D57C347F2B2964
5 changed files with 182 additions and 106 deletions

View File

@ -411,9 +411,19 @@ SELECT
i.issue_number AS issue_number,
i.is_open AS is_open,
i.winner_login AS winner_login,
i.transaction_hash AS transaction_hash,
i.contract_address AS contract_address,
i.confirm_hash AS confirm_hash,
i.execute_hash AS execute_hash,
i.payout_hash AS payout_hash,
i.watch_hash AS watch_hash,
i.payout_receipt AS payout_receipt,
i.commit_sha AS commit_sha,
i.title AS title,
i.comment_id AS comment_id,
i.balance_eth AS balance_eth,
i.tokens AS tokens,
i.value_usd AS value_usd,
i.repo_id AS repo_id,
r.owner AS owner,
r.repo AS repo

View File

@ -1,5 +1,6 @@
(ns commiteth.bounties
(:require [commiteth.db.issues :as issues]
[commiteth.db.bounties :as db-bounties]
[commiteth.db.users :as users]
[commiteth.db.repositories :as repos]
[commiteth.eth.core :as eth]
@ -21,7 +22,50 @@
(let [labels (:labels issue)]
(some #(= label-name (:name %)) labels)))
(defn deploy-contract [owner-address issue-id]
(defn transition [{:keys [issue-id tx-info] :as bounty} state]
(let [bounty-not= (fn [current db]
(some #(not= (%1 current) (%1 db))
(disj (set (keys current)) :tx-info)))
bounty-from-db (issues/get-issue-by-id issue-id)
bounty (and (bounty-not= bounty bounty-from-db)
(merge bounty-from-db bounty))]
(when bounty
(case state
:deploying
(tracker/track-tx! tx-info)
:opened
(do
(tracker/untrack-tx! {:issue-id (:issue-id bounty)
:tx-hash (:transaction-hash bounty)
:result (:contract-address bounty)
:type :deploy})
(github/update-bounty-comment-image bounty))
:pending-sob-confirmation
(tracker/track-tx! tx-info)
:pending-maintainer-confirmation
(tracker/untrack-tx! tx-info)
:paid-with-receipt
(db-bounties/update-payout-receipt issue-id (:payout-receipt bounty))
:watch-set
(tracker/track-tx! tx-info)
:watch-reset
(tracker/untrack-tx! tx-info)
:update-balances
(issues/update-balances (:contract-address bounty)
(:balance-eth bounty)
(:tokens bounty)
(:value-usd bounty))
)
(github/update-comment bounty state))))
(defn deploy-contract [owner-address issue-id]
(if (empty? owner-address)
(log/errorf "issue %s: Unable to deploy bounty contract because repo owner has no Ethereum addres" issue-id)
(try
@ -30,8 +74,8 @@
:internal-tx-id [:deploy issue-id]})]
(do
(log/infof "issue %s: Contract deployed, transaction-hash: %s" issue-id (:tx-hash tx-info))
(github/update-comment (into tx-info [:issue-id issue-id]))
(tracker/track-tx! tx-info))
(transition {:issue-id issue-id
:tx-info tx-info} :deploying))
(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)))))
@ -106,6 +150,7 @@
(if-let [merged-or-paid? (or (:winner_login bounty)
(:payout_hash bounty))]
(cond
(:payout_receipt bounty) :paid-with-receipt
(:payout_hash bounty) :paid
(nil? (:payout_address bounty)) :pending-contributor-address
;; `confirm_hash` is set by us as soon as a PR is merged and the
@ -121,6 +166,8 @@
(seq (:tokens bounty)) :funded
(:contract_address bounty) :opened))))
(comment
(def user 97496)

View File

@ -62,8 +62,42 @@
(:tx-hash @current-tx) (:type @current-tx))
(reset! current-tx nil)))
)
(defrecord ParallelTxTracker [current-txs]
ITxTracker
(try-reserve-nonce [this]
(let [nonce (get-nonce)
monitored-nonces (set (keys current-txs))
first-available-nonce (some #(if (monitored-nonces %1) nil %1) (iterate inc nonce))]
(swap! current-txs assoc nonce nil)))
(drop-nonce [this nonce]
(swap! current-txs dissoc nonce))
(track-tx [this tx-info]
(swap! current-txs update (:nonce tx-info) merge tx-info))
(untrack-tx [this tx-info]
(when (contains? (set (keys current-txs)) (:nonce tx-info))
(swap! current-txs dissoc (:nonce tx-info))))
(prune-txs [this unmined-txs]
(swap! current-txs
(fn [txs]
(let [unmined-tx-hashes (set (map :tx-hash unmined-txs))
time-threshold (t/minus (t/now) (t/minutes 10))
nonces-to-remove
(->> txs
vals
(filter #(or (unmined-tx-hashes (:tx-hash %1))
(and (:timestamp %1)
(t/before? (:timestamp %1) time-threshold))))
(map :nonce))]
(apply dissoc txs nonces-to-remove)))))
)
(def tx-tracker (SequentialTxTracker. (atom nil)))
(defn try-reserve-nonce! []

View File

@ -234,7 +234,7 @@
(str "Contract address: [" addr "](" url-base "/address/" addr ")\n")))
(defn generate-open-comment
[owner repo issue-number contract-address eth-balance eth-balance-str tokens]
[owner repo issue-number contract-address eth-balance tokens]
(let [image-url (md-image "QR Code" (get-qr-url owner repo issue-number eth-balance))
site-url (md-url (server-address) (server-address))]
(format (str "Current balance: %s ETH\n"
@ -247,14 +247,14 @@
(if (on-testnet?)
"To fund it, send test ETH or test ERC20/ERC223 tokens to the contract address."
"To fund it, send ETH or ERC20/ERC223 tokens to the contract address."))
eth-balance-str image-url site-url)))
eth-balance image-url site-url)))
(defn learn-more-text []
(let [site-url (md-url (server-address) (server-address))]
(format "Visit %s to learn more.\n" site-url)))
(defn generate-merged-comment
[contract-address eth-balance-str tokens winner-login winner-address-missing?]
[contract-address eth-balance tokens winner-login winner-address-missing?]
(format (str "Balance: %s ETH\n"
(token-balances-text tokens)
(contract-addr-text contract-address)
@ -264,7 +264,7 @@
"Pending maintainer confirmation") "\n")
"Winner: %s\n"
(learn-more-text))
eth-balance-str winner-login))
eth-balance winner-login))
(defn generate-paid-comment
[contract-address eth-balance-str tokens payee-login]
@ -294,16 +294,16 @@
: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)
(defn update-bounty-comment-image [{:keys [issue-id owner repo issue-number contract-address balance-eth tokens]}]
(let [hash (github-comment-hash owner repo issue-number balance-eth)
issue-url (str owner "/" repo "/issues/" (str issue-number))
png-data (png-rendering/gen-comment-image
contract-address
eth-balance-str
balance-eth
tokens
issue-url)]
(log/debug "update-bounty-comment-image" issue-id owner repo issue-number)
(log/debug contract-address eth-balance-str)
(log/debug contract-address balance-eth)
(log/debug "hash" hash)
(if png-data
@ -331,53 +331,43 @@
(defn update-comment
"Update comment for an open bounty issue"
[{:keys [issue-id owner repo comment-id issue-number contract-address
eth-balance eth-balance-str tokens
balance-eth 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 (and (= state :merged) (empty? winner-address))
(log/warn "issue %s: Cannot sign pending bounty - winner has no payout address" issue-id))
(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))
winner-login winner-address transaction-hash] :as issue}
state]
(let [comment (case state
:deploying
(generate-deploying-comment owner repo issue-number transaction-hash)
:opened
(generate-open-comment owner
repo
issue-number
contract-address
balance-eth
tokens)
(:pending-maintainer-confirmation :pending-contributor-address)
(generate-merged-comment contract-address
balance-eth
tokens
winner-login
(str/blank? winner-address))
:paid-with-receipt
(generate-paid-comment contract-address
balance-eth
tokens
winner-login)
:else nil)]
(log/debug (str "Updating " owner "/" repo "/" issue-number
" comment #" comment-id " with contents: " comment))
(if (= state :deployed)
(if (= state :deploying)
(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))))))
(when 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 get-issue
[owner repo issue-number]

View File

@ -2,9 +2,9 @@
(:require [commiteth.eth.core :as eth]
[commiteth.eth.multisig-wallet :as multisig]
[commiteth.eth.token-data :as token-data]
[commiteth.eth.tracker :as tracker]
[commiteth.github.core :as github]
[commiteth.db.issues :as issues]
[commiteth.eth.tracker :as tracker]
[commiteth.util.util :refer [to-map]]
[taoensso.tufte :as tufte :refer (defnp p profiled profile)]
[commiteth.db.bounties :as db-bounties]
@ -46,34 +46,17 @@
[]
(log/info "In update-issue-contract-address")
(p :update-issue-contract-address
(doseq [{:keys [issue-id transaction-hash]} (issues/list-pending-deployments)]
(log/infof "issue %s: pending deployment: %s" issue-id transaction-hash)
(try
(when-let [receipt (eth/get-transaction-receipt transaction-hash)]
(log/infof "issue %s: update-issue-contract-address: tx receipt: %s" issue-id receipt)
(if-let [contract-address (multisig/find-created-multisig-address receipt)]
(let [_ (tracker/untrack-tx! {:issue-id issue-id
:tx-hash transaction-hash
:result contract-address
:type :deploy})
{:keys [owner repo comment-id issue-number] :as issue}
(issues/get-issue-by-id issue-id)
balance-eth-str (eth/get-balance-eth contract-address 6)
balance-eth (read-string balance-eth-str)
tokens {}]
(log/infof "issue %s: Updating comment" issue-id)
(github/update-comment (to-map issue-id
owner
repo
comment-id
issue-number
contract-address
balance-eth
balance-eth-str
tokens)))
(log/errorf "issue %s: Failed to find contract address in tx logs" issue-id)))
(catch Throwable ex
(log/errorf ex "issue %s: update-issue-contract-address exception:" issue-id)))))
(doseq [{:keys [issue-id transaction-hash] :as issue} (issues/list-pending-deployments)]
(log/infof "issue %s: pending deployment: %s" issue-id transaction-hash)
(try
(when-let [receipt (eth/get-transaction-receipt transaction-hash)]
(log/infof "issue %s: update-issue-contract-address: tx receipt: %s" issue-id receipt)
(if-let [contract-address (multisig/find-created-multisig-address receipt)]
(bounties/transition (assoc issue :contract-address contract-address)
:opened)
(log/errorf "issue %s: Failed to find contract address in tx logs" issue-id)))
(catch Throwable ex
(log/errorf ex "issue %s: update-issue-contract-address exception:" issue-id)))))
(log/info "Exit update-issue-contract-address"))
@ -102,15 +85,18 @@
;; TODO(martin) delete this shortly after org-dashboard deploy
;; as we're now setting `winner_login` when handling a new claims
;; coming in via webhooks (see `commiteth.routes.webhooks/handle-claim`)
(db-bounties/update-winner-login issue-id winner-login)
(let [value (eth/get-balance-hex contract-address)]
(when-not (empty? winner-address)
(let [tx-info (multisig/send-all {:contract contract-address
:payout-address winner-address
:internal-tx-id [:execute issue-id]})]
(log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address winner-address (:tx-hash tx-info))
(tracker/track-tx! tx-info)
(github/update-comment issue))))
;(db-bounties/update-winner-login issue-id winner-login)
(if (empty? winner-address)
(do
(log/warn "issue %s: Cannot sign pending bounty - winner has no payout address" issue-id)
(bounties/transition {:issue-id issue-id} :pending-contributor-address))
(let [tx-info (multisig/send-all {:contract contract-address
:payout-address winner-address
:internal-tx-id [:execute issue-id]})]
(log/infof "issue %s: Payout self-signed, called sign-all(%s) tx: %s" issue-id contract-address winner-address (:tx-hash tx-info))
(bounties/transition {:execute-hash (:tx-hash tx-info)
:issue-id issue-id
:tx-info tx-info} :pending-sob-confirmation)))
(catch Throwable ex
(log/error ex "issue %s: self-sign-bounty exception" issue-id)))))
(log/info "Exit self-sign-bounty"))
@ -127,10 +113,14 @@
(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)
(tracker/untrack-tx! {:issue-id issue-id
:tx-hash execute-hash
:result confirm-hash
:type :execute})))
(bounties/transition {:issue-id issue-id
:tx-info {:issue-id issue-id
:tx-hash execute-hash
:result confirm-hash
:type :execute}}
:pending-maintainer-confirmation)
))
(catch Throwable ex
(log/errorf ex "issue %s: update-confirm-hash exception:" issue-id))) )
(log/info "Exit update-confirm-hash")))
@ -144,10 +134,12 @@
(log/infof "issue %s: pending watch call %s" issue-id watch-hash)
(try
(when-let [receipt (eth/get-transaction-receipt watch-hash)]
(tracker/untrack-tx! {:issue-id issue-id
:tx-hash watch-hash
:result nil
:type :watch}))
(bounties/transition {:issue-id issue-id
:tx-info
{:issue-id issue-id
:tx-hash watch-hash
:result nil
:type :watch}} :watch-reset))
(catch Throwable ex
(log/errorf ex "issue %s: update-watch-hash exception:" issue-id))
))))
@ -182,10 +174,9 @@
(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-hash)]
(log/infof "issue %s: execute tx: %s" issue-id execute-tx-hash))))
(do
(log/infof "issue %s: Payout has succeeded, payout receipt %s" issue-id receipt)
(github/update-comment (assoc issue :payout-receipt receipt)))))
(bounties/transition (assoc issue :payout-receipt receipt) :paid-with-receipt))))
(when (older-than-3h? updated)
(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)))
@ -222,7 +213,9 @@
(let [tx-info (multisig/watch-token {:bounty-addr bounty-addr
:token tla
:internal-tx-id [:watch issue-id]})]
(tracker/track-tx! tx-info)))))))
(bounties/transition {:issue-id issue-id
:tx-info tx-info}
:watch-set)))))))
(catch Throwable ex
(log/error ex "bounty %s: update-bounty-token-balances exception" bounty-addr))))
(log/info "Exit update-bounty-token-balances"))
@ -278,12 +271,14 @@
(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))
(bounties/transition {:issue-id issue-id
:balance-eth current-balance-eth
:tokens token-balances
:value-usd (fiat-util/bounty-usd-value
(merge token-balances {:ETH current-balance-eth}))} :update-balances)
(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
(log/error ex "issue %s: update-balances exception" issue-id)))))
(log/info "Exit update-balances"))