Bitcoin/public point to address: Difference between revisions
Content added Content deleted
m (Formatting.) |
(add Common Lisp version) |
||
Line 99: | Line 99: | ||
return 0; |
return 0; |
||
}</lang> |
}</lang> |
||
=={{header|Common Lisp}}== |
|||
{{libheader|Quicklisp}} |
|||
{{libheader|Ironclad}} |
|||
<lang lisp> |
|||
;;; package definition |
|||
(cl:defpackage :bitcoin-address-encoder |
|||
(:use :cl . #.(ql:quickload :ironclad)) |
|||
(:shadowing-import-from :ironclad #:null) |
|||
(:import-from :ironclad #:simple-octet-vector)) |
|||
(cl:in-package :bitcoin-address-encoder) |
|||
;;; secp256k1 |
|||
(macrolet ((define-constants (&rest constants) |
|||
`(progn ,@(loop for (name value) on constants by #'cddr |
|||
collect `(defconstant ,name ,value))))) |
|||
(define-constants |
|||
;; these constants are only necessary for computing public keys |
|||
xg #x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 |
|||
yg #x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 |
|||
ng #xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 |
|||
;; this constant is necessary both for computation and validation |
|||
p #.`(- ,@(mapcar (lambda (n) (ash 1 n)) '(256 32 9 8 7 6 4 0))) |
|||
)) |
|||
;;; operations within the field of positive integers modulo the prime p |
|||
(macrolet ((define-operations (&rest pairs) |
|||
`(progn ,@(loop for (op name) on pairs by #'cddr collect |
|||
`(defun ,name (x y) (mod (,op x y) p)))))) |
|||
(define-operations + add - sub * mul)) |
|||
;;; modular exponentiation by squaring, still in the same field |
|||
;;; THIS IS A VARIABLE-TIME ALGORITHM, BLIND REUSE IS INSECURE! |
|||
(defun pow (x n &optional (x^n 1)) ; (declare (notinline pow)) |
|||
(do ((x x (mul x x)) (n n (ash n -1))) ((zerop n) x^n) |
|||
(when (oddp n) (setf x^n (mul x^n x))))) |
|||
;;; extended euclidean algorithm, still in the same field |
|||
;;; THIS IS A VARIABLE-TIME ALGORITHM, BLIND REUSE IS INSECURE! |
|||
(defun eea (a b &optional (x 0) (prevx 1) (y 1) (prevy 0)) |
|||
(if (zerop b) (values prevx prevy) |
|||
(multiple-value-bind (q r) (floor a b) |
|||
(eea b r (sub prevx (mul q x)) x (sub prevy (mul q y)) y)))) |
|||
;;; multiplicative inverse in the field of integers modulo the prime p |
|||
(defun inv (x) (nth-value 1 (eea p (mod x p)))) |
|||
;;; operation, in the group of rational points over elliptic curve "SECP256K1" |
|||
;;; THIS IS A VARIABLE-TIME ALGORITHM, BLIND REUSE IS INSECURE! |
|||
(defun addp (xp yp xq yq) ; https://hyperelliptic.org/EFD/g1p/auto-shortw.html |
|||
(if (and xp yp xq yq) ; base case: avoid The Pothole At The End Of The Algebra |
|||
(macrolet ((ua (s r) `(let* ((s ,s) (x (sub (mul s s) ,r))) |
|||
(values x (sub 0 (add yp (mul s (sub x xp)))))))) |
|||
(if (/= xp xq) (ua (mul (sub yp yq) (inv (- xp xq))) (add xp xq)) ; p+q |
|||
(if (zerop (add yp yq)) (values nil nil) ; p = -q, so p+q = infinity |
|||
(ua (mul (inv (* 2 yp)) (mul 3 (pow xp 2))) (mul 2 xp))))) ; 2*p |
|||
(if (and xp yp) (values xp yp) (values xq yq)))) ; pick the [in]finite one |
|||
;;; Scalar multiplication (by doubling) |
|||
;;; THIS IS A VARIABLE-TIME ALGORITHM, BLIND REUSE IS INSECURE! |
|||
(defun smulp (k xp yp) |
|||
(if (zerop k) (values nil nil) |
|||
(multiple-value-bind (xq yq) (addp xp yp xp yp) |
|||
(multiple-value-bind (xr yr) (smulp (ash k -1) xq yq) |
|||
(if (evenp k) (values xr yr) (addp xp yp xr yr)))))) |
|||
;;; Tests if a point is on the curve |
|||
;;; THIS IS A VARIABLE-TIME ALGORITHM, BLIND REUSE IS INSECURE! |
|||
(defun e (x y) (= (mul y y) (add (pow x #o3) #o7))) |
|||
;;; "A horseshoe brings good luck even to those of little faith." - S. Nakamoto |
|||
(macrolet ((check-sanity (&rest checks) |
|||
`(progn ,@(loop for (test text) on checks by #'cddr |
|||
collect `(assert ,test () ,text))))) |
|||
(check-sanity (= 977 (sub (pow 2 256) (pow 2 32))) "mathematics has broken" |
|||
(e xg yg) "the generator isn't a rational point on the curve" |
|||
(not (smulp ng xg yg)) "the generator's order is incorrect")) |
|||
;;; dyslexia-friendly encoding, placed in public domain by Satoshi Nakamoto |
|||
(defun base58enc (bytes) |
|||
(loop with code = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" |
|||
for x = (octets-to-integer bytes) then (floor x #10R58) until (zerop x) |
|||
collect (char code (mod x #10R58)) into out finally |
|||
(return (coerce (nreverse (append out (loop for b across bytes |
|||
while (zerop b) collect #\1))) |
|||
'string)))) |
|||
;;; encodes arbitrary coordinates into a Pay-To-Pubkey-Hash address |
|||
(defun pubkey-to-p2pkh (x y) |
|||
(assert (e x y) (x y) "The point (~D, ~D) is off the curve secp256k1." x y) |
|||
(labels ((digest (hashes bytes) |
|||
(reduce 'digest-sequence hashes :from-end t :initial-value bytes)) |
|||
(sovcat (&rest things) |
|||
(apply 'concatenate 'simple-octet-vector things)) |
|||
(checksum (octets) |
|||
(sovcat octets (subseq (digest '(sha256 sha256) octets) 0 4)))) |
|||
(let ((point (sovcat '(4) (integer-to-octets x) (integer-to-octets y)))) |
|||
(base58enc (checksum (sovcat '(0) (digest '(ripemd-160 sha256) point))))))) |
|||
;; ? (pubkey-to-p2pkh |
|||
;; #x50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352 |
|||
;; #x2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6) |
|||
;; |
|||
;; "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM" |
|||
</lang> |
|||
=={{header|D}}== |
=={{header|D}}== |