Add check-tx-receipts thread; allow only one tx submission at a time

This commit is contained in:
Vitaliy Vlasov 2018-04-23 19:36:33 +03:00
parent 183a50dc94
commit 03cf011f01
5 changed files with 103 additions and 23 deletions

View File

@ -145,7 +145,8 @@ RETURNING repo_id, issue_id, issue_number, title, commit_sha, contract_address;
-- :name update-transaction-hash :! :n
-- :doc updates transaction-hash for a given issue
UPDATE issues
SET transaction_hash = :transaction_hash
SET transaction_hash = :transaction_hash,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
@ -241,6 +242,48 @@ WHERE pr_id = :pr_id;
-- Bounties ------------------------------------------------------------------------
-- :name unmined-tx-hashes :? :*
-- :doc hashes that haven't been mined for some time
SELECT
CASE WHEN transaction_hash is not null and contract_address is null
THEN transaction_hash
WHEN execute_hash is not null and confirm_hash is null
THEN execute_hash
WHEN watch_hash is not null
THEN watch_hash
END
AS tx_hash,
CASE WHEN transaction_hash is not null and contract_address is null
THEN 'deploy'
WHEN execute_hash is not null and confirm_hash is null
THEN 'execute'
WHEN watch_hash is not null
THEN 'watch'
END
AS type,
issue_id
FROM issues
WHERE updated < timezone('utc'::text, now()) - interval '5 minutes'
AND (transaction_hash is not null and contract_address is null
OR execute_hash is not null and confirm_hash is null
OR watch_hash is not null);
-- :name reset-tx-hash! :! :n
-- :doc reset tx hash if it hasn't been mined for some time
UPDATE issues
SET
--~ (when (= (:type params) "deploy") "transaction_hash")
--~ (when (= (:type params) "execute") "execute_hash")
--~ (when (= (:type params) "watch") "watch_hash")
= null
WHERE
--~ (when (= (:type params) "deploy") "transaction_hash")
--~ (when (= (:type params) "execute") "execute_hash")
--~ (when (= (:type params) "watch") "watch_hash")
= :tx-hash
-- :name pending-contracts :? :*
-- :doc bounty issues where deploy contract has failed
SELECT
@ -254,7 +297,7 @@ FROM issues i, users u, repositories r
WHERE
r.user_id = u.id
AND i.repo_id = r.repo_id
AND (i.transaction_hash IS NULL OR updated > now() - interval '1 hour')
AND i.transaction_hash IS NULL
AND i.contract_address IS NULL;

View File

@ -100,3 +100,12 @@
[]
(jdbc/with-db-connection [con-db *db*]
(db/bounties-activity con-db)))
(defn unmined-tx-hashes []
(jdbc/with-db-connection [con-db *db*]
(db/unmined-tx-hashes con-db)))
(defn reset-tx-hash!
[tx-hash type]
(jdbc/with-db-connection [con-db *db*]
(db/reset-tx-hash! con-db {:tx-hash tx-hash :type type})))

View File

@ -7,6 +7,7 @@
[taoensso.tufte :as tufte :refer (defnp p profiled profile)]
[clojure.tools.logging :as log]
[clojure.string :as str]
[clj-time.core :as t]
[mount.core :as mount]
[pandect.core :as pandect]
[commiteth.util.util :refer [json-api-request]])
@ -51,26 +52,20 @@
(throw (ex-info "Make sure you provided proper credentials in appropriate resources/config.edn"
{:password password :file-path file-path}))))))
(def highest-nonce (atom nil))
(def nonces-dropped (atom (clojure.lang.PersistentQueue/EMPTY)))
(defrecord TxNonce [tx-hash nonce type timestamp])
(def nonce-being-mined (atom nil))
(defn get-nonce []
(if (seq @nonces-dropped)
(let [nonce (peek @nonces-dropped)]
(swap! nonces-dropped pop)
nonce)
(let [nonce (.. (.ethGetTransactionCount @web3j-obj
(env :eth-account)
DefaultBlockParameterName/LATEST)
DefaultBlockParameterName/PENDING)
sendAsync
get
getTransactionCount)]
(if (or (nil? @highest-nonce)
(> nonce @highest-nonce))
(reset! highest-nonce nonce)
(swap! highest-nonce (comp biginteger inc))))))
(if (or (nil? @nonce-being-mined)
(> nonce (:nonce @nonce-being-mined)))
(:nonce (reset! nonce-being-mined (TxNonce. nil nonce nil nil)))
(throw (Exception. (str "Attempting to re-use old nonce" nonce))))))
(defn get-signed-tx [gas-price gas-limit to data nonce]
"Create a sign a raw transaction.
@ -248,8 +243,13 @@
(defn get-transaction-receipt
[hash]
(eth-rpc {:method "eth_getTransactionReceipt"
:params [hash]}))
(when-let [receipt (eth-rpc {:method "eth_getTransactionReceipt"
:params [hash]})]
(if (= hash (:tx-hash @nonce-being-mined))
(reset! nonce-being-mined nil)
;; This can happen in case of payout receipts
(log/infof "Obtained receipt for tx not currently being mined: " hash))
receipt))
(defn format-call-params
[method-id & params]
@ -263,7 +263,7 @@
:params [{:to contract :data data} "latest"]})))
(defn execute
[{:keys [from contract method-id gas-limit params internal-tx-id]}]
[{:keys [from contract method-id gas-limit params internal-tx-id type]}]
{:pre [(string? method-id)]}
(let [data (apply format-call-params method-id params)
gas-price (gas-price)
@ -288,12 +288,16 @@
(assoc params :gas gas))]
(if (offline-signing?)
(try
(eth-rpc
{:method "eth_sendRawTransaction"
:params [params]
:internal-tx-id internal-tx-id})
(let [tx-hash (eth-rpc
{:method "eth_sendRawTransaction"
:params [params]
:internal-tx-id internal-tx-id})]
(:tx-hash (swap! nonce-being-mined
assoc :tx-hash tx-hash
:type type
:timestamp (t/now))))
(catch Throwable ex
(swap! nonces-dropped conj nonce)
(reset! nonce-being-mined nil)
(throw ex)))
(eth-rpc
{:method "personal_sendTransaction"

View File

@ -43,6 +43,7 @@
[{:keys [owner internal-tx-id]}]
{:pre [(string? owner) (string? internal-tx-id)]}
(eth/execute {:internal-tx-id internal-tx-id
:type "deploy"
:from (eth/eth-account)
:contract (factory-contract-addr)
:method-id (:create method-ids)
@ -93,6 +94,7 @@
(:withdraw-everything method-ids)
payout-address)]
(eth/execute {:internal-tx-id internal-tx-id
:type "execute"
:from (eth/eth-account)
:contract contract
:method-id (:submit-transaction method-ids)
@ -111,6 +113,7 @@
(let [token-address (get-token-address token)]
(assert token-address)
(eth/execute {:internal-tx-id (str "watch-token-" (System/currentTimeMillis) "-" bounty-addr)
:type "watch"
:from (eth/eth-account)
:contract bounty-addr
:method-id (:watch method-ids)

View File

@ -89,6 +89,7 @@
(p :deploy-pending-contracts
(doseq [{issue-id :issue_id
issue-number :issue_number
transaction-hash :transaction_hash
owner :owner
owner-address :owner_address
repo :repo} (db-bounties/pending-contracts)]
@ -371,6 +372,25 @@
(log/error ex "issue %s: update-balances exception" issue-id)))))
(log/info "Exit update-balances"))
(defn check-tx-receipts
"At all times, there should be no more than one unmined tx hash,
as we are executing txs sequentially"
[]
(log/info "In check-tx-receipts")
(doseq [{tx-hash :tx_hash
type :type
issue-id :issue_id} (db-bounties/unmined-tx-hashes)]
(log/infof "issue %s: resetting tx operation: %s for hash: %s" issue-id type tx-hash)
(db-bounties/reset-tx-hash! tx-hash type)
(when (= tx-hash (:tx-hash @eth/nonce-being-mined))
(log/infof "issue %s: reset nonce" issue-id)
(reset! eth/nonce-being-mined nil)))
(when (and (:timestamp @eth/nonce-being-mined)
(t/before? (:timestamp @eth/nonce-being-mined) (t/minus (t/now) (t/minutes 5))))
(log/errorf "nonce-being-mined not present in DB and unmined for 5 minutes, force reset. Tx hash: %s, type: %s"
(:tx-hash @eth/nonce-being-mined) (:type @eth/nonce-being-mined))
(reset! eth/nonce-being-mined nil))
(log/info "Exit check-tx-receipts"))
(defn wrap-in-try-catch [func]
(try
@ -394,6 +414,7 @@
update-confirm-hash
update-payout-receipt
update-watch-hash
check-tx-receipts
self-sign-bounty
])
(log/info "run-1-min-interval-tasks done")))