From 1663669163fcd031fb020db5a188725229240cbf Mon Sep 17 00:00:00 2001 From: Teemu Patja Date: Tue, 21 Feb 2017 21:41:39 +0200 Subject: [PATCH] Ethereum address validation + unit test * utility function for validate Ethereum addresses * unit test --- project.clj | 3 +- src/clj/commiteth/eth/core.clj | 50 ++++++++++++++++++++++++++++++--- test/clj/commiteth/test/eth.clj | 14 +++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 test/clj/commiteth/test/eth.clj diff --git a/project.clj b/project.clj index 28d30ed..de02375 100644 --- a/project.clj +++ b/project.clj @@ -40,7 +40,8 @@ [crypto-random "1.2.0"] [crypto-equality "1.0.0"] [cheshire "5.7.0"] - [mpg "1.3.0"]] + [mpg "1.3.0"] + [pandect "0.6.1"]] :min-lein-version "2.0.0" :source-paths ["src/clj" "src/cljc"] diff --git a/src/clj/commiteth/eth/core.clj b/src/clj/commiteth/eth/core.clj index a2d7b3d..949df4d 100644 --- a/src/clj/commiteth/eth/core.clj +++ b/src/clj/commiteth/eth/core.clj @@ -5,7 +5,8 @@ [commiteth.config :refer [env]] [clojure.string :refer [join]] [clojure.tools.logging :as log] - [clojure.string :as str])) + [clojure.string :as str] + [pandect.core :as pandect])) (def eth-rpc-url "http://localhost:8545") (defn eth-account [] (:eth-account env)) @@ -65,9 +66,9 @@ (eth-rpc "personal_sendTransaction" [(if (not (nil? to)) - (merge args {:to to}) - args) - (eth-password)]))) + (merge args {:to to}) + args) + (eth-password)]))) (defn get-transaction-receipt [hash] @@ -104,3 +105,44 @@ (let [data (apply format-call-params method-id params) value (format "0x%x" 1)] (send-transaction from contract value {:data data}))) + + +(defn hex-ch->num + [ch] + (read-string (str "0x" ch))) + +(defn strip-0x + [x] + (str/replace x #"(?i)^0x" "")) + +(defn upper-ch [ch] + (first (str/upper-case ch))) + +(defn lower-ch [ch] + (first (str/lower-case ch))) + +(defn valid-address? + "Validate given ethereum address. Checksum validation is performed + and input is case-sensitive" + [address] + (log/debug "valid-address?" address) + ;; logic based on + ;; https://github.com/cilphex/ethereum-address/blob/master/index.js + (and (boolean (re-matches #"(?i)^(0x)?[0-9a-f]{40}$" address)) + (let [addr (strip-0x address) + hash (pandect/keccak-256 (str/lower-case addr))] + (log/debug "address hash" hash "addr" addr) + (->> + (map-indexed (fn [idx _] + (let [hash-ch-int (-> (nth hash idx) + (hex-ch->num)) + ch (nth addr idx) + ch-lower (lower-ch ch) + ch-upper (upper-ch ch)] + (or (and (> hash-ch-int 7) + (not= ch-upper ch)) + (and (<= hash-ch-int 7) + (not= ch-lower ch))))) + addr) + (filter true?) + (empty?))))) diff --git a/test/clj/commiteth/test/eth.clj b/test/clj/commiteth/test/eth.clj new file mode 100644 index 0000000..e28e08b --- /dev/null +++ b/test/clj/commiteth/test/eth.clj @@ -0,0 +1,14 @@ +(ns commiteth.test.eth + (:require [clojure.test :refer :all] + [commiteth.eth.core :as eth])) + +(deftest test-address-validation + (testing "Short address is invalid" + (let [addr "0x34264362346364326423"] + (is (false? (eth/valid-address? addr))))) + (testing "Valid address is valid" + (let [addr "0xA1cab91b36bea34487c5670Bbd00a1Aa8196aeD8"] + (is (true? (eth/valid-address? addr))))) + (testing "Case sensitivity matters" + (let [addr "0xa1cab91b36bea34487c5670bbd00a1aa8196aed8"] + (is (false? (eth/valid-address? addr))))))