diff --git a/.gitignore b/.gitignore index 623089d..85d77f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea test.txt test.php - +vendor +**/__pycache__/ +Blockchain/Python_Package/virtual_env/ \ No newline at end of file diff --git a/Blockchain/Backend/API/Client_calls/BaseCurl.php b/Blockchain/Backend/API/Client_calls/BaseCurl.php new file mode 100644 index 0000000..e347860 --- /dev/null +++ b/Blockchain/Backend/API/Client_calls/BaseCurl.php @@ -0,0 +1,37 @@ +true, "errorType" => "Curl Error", "data"=>curl_error($curlObj)]; + } + elseif ($httpCode >= 400) + { + return ["error" => true, "errorType" => "HTTP Error", "data" => $httpCode]; + } else + { + return ["data" => $response]; + } + } + + public static function closeCurl($curlObj) + { + curl_close($curlObj); + } +} \ No newline at end of file diff --git a/Blockchain/Backend/API/Client_calls/ClientAPI.php b/Blockchain/Backend/API/Client_calls/ClientAPI.php new file mode 100644 index 0000000..6bf79f1 --- /dev/null +++ b/Blockchain/Backend/API/Client_calls/ClientAPI.php @@ -0,0 +1,25 @@ +true, "errorType" => "Curl Error", "data" => curl_error($curlObject)]; + } else { + return ["data" => $response]; + } + } + + public static function errorHandleResponse($data): bool + { + return array_key_exists('error', $data); + } + + +} \ No newline at end of file diff --git a/Blockchain/Backend/util/util.php b/Blockchain/Backend/util/util.php index 1b26888..9bd1b95 100644 --- a/Blockchain/Backend/util/util.php +++ b/Blockchain/Backend/util/util.php @@ -2,4 +2,18 @@ function hash256($s) { // Two rounds of SHA256 return hash('sha256', hash('sha256', $s, true), true); -} \ No newline at end of file +} + +function hash160($data) { + // Using RIPEMD160 with HMAC-SHA256 + $sha256Hash = hash('sha256', $data, true); + return hash('ripemd160', $sha256Hash, true); +} + +// Example usage +$input = 'some data to hash'; +$hashed256 = hash256($input); +$hashed160 = hash160($input); + +echo "Hashed 256: " . bin2hex($hashed256) . PHP_EOL; +echo "Hashed 160: " . bin2hex($hashed160) . PHP_EOL; \ No newline at end of file diff --git a/Blockchain/Client/Account.php b/Blockchain/Client/Account.php new file mode 100644 index 0000000..42b7970 --- /dev/null +++ b/Blockchain/Client/Account.php @@ -0,0 +1,16 @@ += prime or num < 0: + error = "Num {} not in field range 0 to {}".format(num, prime - 1) + raise ValueError(error) + self.num = num + self.prime = prime + + def __repr__(self): + return "FieldElement_{}({})".format(self.prime, self.num) + + def __eq__(self, other): + if other is None: + return False + return self.num == other.num and self.prime == other.prime + + def __ne__(self, other): + # this should be the inverse of the == operator + return not (self == other) + + def __add__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot add two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + num = (self.num + other.num) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __sub__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot subtract two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + num = (self.num - other.num) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __mul__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot multiply two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + num = (self.num * other.num) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __pow__(self, exponent): + n = exponent % (self.prime - 1) + num = pow(self.num, n, self.prime) + + return self.__class__(num, self.prime) + + def __truediv__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot divide two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + # use fermat's little theorem: + # self.num**(p-1) % p == 1 + # this means: + # 1/n == pow(n, p-2, p) + num = (self.num * pow(other.num, self.prime - 2, self.prime)) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __rmul__(self, coefficient): + num = (self.num * coefficient) % self.prime + return self.__class__(num=num, prime=self.prime) + + +class Point: + def __init__(self, x, y, a, b): + self.a = a + self.b = b + self.x = x + self.y = y + if self.x is None and self.y is None: + return + if self.y ** 2 != self.x ** 3 + a * x + b: + raise ValueError("({}, {}) is not on the curve".format(x, y)) + + # end::source1[] + + def __eq__(self, other): + return ( + self.x == other.x + and self.y == other.y + and self.a == other.a + and self.b == other.b + ) + + def __ne__(self, other): + # this should be the inverse of the == operator + return not (self == other) + + def __repr__(self): + if self.x is None: + return "Point(infinity)" + elif isinstance(self.x, FieldElement): + return "Point({},{})_{}_{} FieldElement({})".format( + self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime + ) + else: + return "Point({},{})_{}_{}".format(self.x, self.y, self.a, self.b) + + def __add__(self, other): + if self.a != other.a or self.b != other.b: + raise TypeError( + "Points {}, {} are not on the same curve".format(self, other) + ) + # Case 0.0: self is the point at infinity, return other + if self.x is None: + return other + # Case 0.1: other is the point at infinity, return self + if other.x is None: + return self + + # Case 1: self.x == other.x, self.y != other.y + # Result is point at infinity + if self.x == other.x and self.y != other.y: + return self.__class__(None, None, self.a, self.b) + + # Case 2: self.x ≠ other.x + # Formula (x3,y3)==(x1,y1)+(x2,y2) + # s=(y2-y1)/(x2-x1) + # x3=s**2-x1-x2 + # y3=s*(x1-x3)-y1 + if self.x != other.x: + s = (other.y - self.y) / (other.x - self.x) + x = s ** 2 - self.x - other.x + y = s * (self.x - x) - self.y + return self.__class__(x, y, self.a, self.b) + + # Case 4: if we are tangent to the vertical line, when x1 = x2 and y1,y2 = 0 + # we return the point at infinity + # note instead of figuring out what 0 is for each type + # we just use 0 * self.x + if self == other and self.y == 0 * self.x: + return self.__class__(None, None, self.a, self.b) + + # Case 3: self == other + # Formula (x3,y3)=(x1,y1)+(x1,y1) + # s=(3*x1**2+a)/(2*y1) + # x3=s**2-2*x1 + # y3=s*(x1-x3)-y1 + if self == other: + s = (3 * self.x ** 2 + self.a) / (2 * self.y) + x = s ** 2 - 2 * self.x + y = s * (self.x - x) - self.y + return self.__class__(x, y, self.a, self.b) + + # tag::source3[] + def __rmul__(self, coefficient): + coef = coefficient + current = self # <1> + result = self.__class__(None, None, self.a, self.b) # <2> + while coef: + if coef & 1: # <3> + result += current + current += current # <4> + coef >>= 1 # <5> + return result + + +class Sha256Field(FieldElement): + def __init__(self, num, prime=None): + super().__init__(num=num, prime=P) + + def __repr__(self): + return "{:x}".format(self.num).zfill(64) + + def sqrt(self): + return self ** ((P + 1) // 4) + + +class Sha256Point(Point): + def __init__(self, x, y, a=None, b=None): + a, b = Sha256Field(A), Sha256Field(B) + if type(x) == int: + super().__init__(x=Sha256Field(x), y=Sha256Field(y), a=a, b=b) + else: + super().__init__(x=x, y=y, a=a, b=b) # <1> + + # end::source7[] + + def __repr__(self): + if self.x is None: + return "Sha256Point(infinity)" + else: + return "Sha256Point({}, {})".format(self.x, self.y) + + # tag::source8[] + def __rmul__(self, coefficient): + coef = coefficient % N # <1> + return super().__rmul__(coef) + + # end::source8[] + + # tag::source12[] + def verify(self, z, sig): + s_inv = pow(sig.s, N - 2, N) # <1> + u = z * s_inv % N # <2> + v = sig.r * s_inv % N # <3> + total = u * G + v * self # <4> + return total.x.num == sig.r # <5> + + # end::source12[] + def sec(self, compressed=True): + """returns the binary version of the SEC format""" + if compressed: + if self.y.num % 2 == 0: + return b"\x02" + self.x.num.to_bytes(32, "big") + else: + return b"\x03" + self.x.num.to_bytes(32, "big") + else: + return ( + b"\x04" + + self.x.num.to_bytes(32, "big") + + self.y.num.to_bytes(32, "big") + ) + + # get sha256 hash of compressed public key from sec() + # then do ripemd160 of that hash + def hash160(self, compressed=True): + return RIPEMD160.new(hashlib.sha256(self.sec(compressed)).digest()).digest() + + def encode_base58(self, s): + count = 0 + for c in s: # <1> + if c == 0: + count += 1 + else: + break + num = int.from_bytes(s, "big") + prefix = "1" * count + result = "" + while num > 0: # <2> + num, mod = divmod(num, 58) + result = BASE58_ALPHABET[mod] + result + return prefix + result # <3> + + def encode_base58_checksum(self, b): + return self.encode_base58( + b + (hashlib.sha256(hashlib.sha256(b).digest()).digest())[:4] + ) + + def address(self, compressed=True, testnet=False): + """Returns the address string""" + h160 = self.hash160(compressed) + if testnet: + prefix = b"\x6f" + else: + prefix = b"\x00" + return self.encode_base58_checksum(prefix + h160) + + @classmethod + def parse(self, sec_bin): + """returns a Point object from a SEC binary (not hex)""" + if sec_bin[0] == 4: # <1> + x = int.from_bytes(sec_bin[1:33], "big") + y = int.from_bytes(sec_bin[33:65], "big") + return Sha256Point(x=x, y=y) + is_even = sec_bin[0] == 2 # <2> + x = Sha256Field(int.from_bytes(sec_bin[1:], "big")) + # right side of the equation y^2 = x^3 + 7 + alpha = x ** 3 + Sha256Field(B) + # solve for left side + beta = alpha.sqrt() # <3> + if beta.num % 2 == 0: # <4> + even_beta = beta + odd_beta = Sha256Field(P - beta.num) + else: + even_beta = Sha256Field(P - beta.num) + odd_beta = beta + if is_even: + return Sha256Point(x, even_beta) + else: + return Sha256Point(x, odd_beta) + + +G = Sha256Point( + 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, + 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, +) + + +class Signature: + def __init__(self, r, s): + self.r = r + self.s = s + + def __repr__(self): + return "Signature({:x},{:x})".format(self.r, self.s) + + def der(self): + rbin = self.r.to_bytes(32, byteorder="big") + # remove all null bytes at the beginning + rbin = rbin.lstrip(b"\x00") + # if rbin has a high bit, add a \x00 + if rbin[0] & 0x80: + rbin = b"\x00" + rbin + result = bytes([2, len(rbin)]) + rbin # <1> + sbin = self.s.to_bytes(32, byteorder="big") + # remove all null bytes at the beginning + sbin = sbin.lstrip(b"\x00") + # if sbin has a high bit, add a \x00 + if sbin[0] & 0x80: + sbin = b"\x00" + sbin + result += bytes([2, len(sbin)]) + sbin + return bytes([0x30, len(result)]) + result + + @classmethod + def parse(cls, signature_bin): + s = BytesIO(signature_bin) + compound = s.read(1)[0] + if compound != 0x30: + raise SyntaxError("Bad Signature") + length = s.read(1)[0] + if length + 2 != len(signature_bin): + raise SyntaxError("Bad Signature Length") + marker = s.read(1)[0] + if marker != 0x02: + raise SyntaxError("Bad Signature") + rlength = s.read(1)[0] + r = int.from_bytes(s.read(rlength), "big") + marker = s.read(1)[0] + if marker != 0x02: + raise SyntaxError("Bad Signature") + slength = s.read(1)[0] + s = int.from_bytes(s.read(slength), "big") + if len(signature_bin) != 6 + rlength + slength: + raise SyntaxError("Signature too long") + return cls(r, s) + + +class PrivateKey: + def __init__(self, secret): + self.secret = secret + self.point = secret * G # <1> + + def hex(self): + return "{:x}".format(self.secret).zfill(64) + + # end::source13[] + + # tag::source14[] + def sign(self, z): + k = self.deterministic_k(z) # <1> + r = (k * G).x.num + k_inv = pow(k, N - 2, N) + s = (z + r * self.secret) * k_inv % N + if s > N / 2: + s = N - s + return Signature(r, s) + + def deterministic_k(self, z): + k = b"\x00" * 32 + v = b"\x01" * 32 + if z > N: + z -= N + z_bytes = z.to_bytes(32, "big") + secret_bytes = self.secret.to_bytes(32, "big") + s256 = hashlib.sha256 + k = hmac.new(k, v + b"\x00" + secret_bytes + z_bytes, s256).digest() + v = hmac.new(k, v, s256).digest() + k = hmac.new(k, v + b"\x01" + secret_bytes + z_bytes, s256).digest() + v = hmac.new(k, v, s256).digest() + while True: + v = hmac.new(k, v, s256).digest() + candidate = int.from_bytes(v, "big") + if candidate >= 1 and candidate < N: + return candidate # <2> + k = hmac.new(k, v + b"\x00", s256).digest() + v = hmac.new(k, v, s256).digest() + + # end::source14[] diff --git a/Blockchain/Python_Package/EllepticCurve/FieldElement.py b/Blockchain/Python_Package/EllepticCurve/FieldElement.py new file mode 100644 index 0000000..0f047de --- /dev/null +++ b/Blockchain/Python_Package/EllepticCurve/FieldElement.py @@ -0,0 +1,75 @@ +""" +Copyright (c) 2021 Codiesalert.com +These scripts shouldn't be used for commercial purpose without Codies Alert Permission +Any violations may lead to legal action +""" + + +class FieldElement: + def __init__(self, num, prime): + if num >= prime or num < 0: + error = "Num {} not in field range 0 to {}".format(num, prime - 1) + raise ValueError(error) + self.num = num + self.prime = prime + + def __repr__(self): + return "FieldElement_{}({})".format(self.prime, self.num) + + def __eq__(self, other): + if other is None: + return False + return self.num == other.num and self.prime == other.prime + + def __ne__(self, other): + # this should be the inverse of the == operator + return not (self == other) + + def __add__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot add two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + num = (self.num + other.num) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __sub__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot subtract two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + num = (self.num - other.num) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __mul__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot multiply two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + num = (self.num * other.num) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __pow__(self, exponent): + n = exponent % (self.prime - 1) + num = pow(self.num, n, self.prime) + return self.__class__(num, self.prime) + + def __truediv__(self, other): + if self.prime != other.prime: + raise TypeError("Cannot divide two numbers in different Fields") + # self.num and other.num are the actual values + # self.prime is what we need to mod against + # use fermat's little theorem: + # self.num**(p-1) % p == 1 + # this means: + # 1/n == pow(n, p-2, p) + num = (self.num * pow(other.num, self.prime - 2, self.prime)) % self.prime + # We return an element of the same class + return self.__class__(num, self.prime) + + def __rmul__(self, coefficient): + num = (self.num * coefficient) % self.prime + return self.__class__(num=num, prime=self.prime) diff --git a/Blockchain/Python_Package/EllepticCurve/Point.py b/Blockchain/Python_Package/EllepticCurve/Point.py new file mode 100644 index 0000000..e7ef8f5 --- /dev/null +++ b/Blockchain/Python_Package/EllepticCurve/Point.py @@ -0,0 +1,100 @@ +""" +Copyright (c) 2021 Codiesalert.com +These scripts should be used for commercial purpose without Codies Alert Permission +Any violations may lead to legal action +""" +from FieldElement import FieldElement + + +class Point: + def __init__(self, x, y, a, b): + self.a = a + self.b = b + self.x = x + self.y = y + if self.x is None and self.y is None: + return + if self.y ** 2 != self.x ** 3 + a * x + b: + raise ValueError("({}, {}) is not on the curve".format(x, y)) + + # end::source1[] + + def __eq__(self, other): + return ( + self.x == other.x + and self.y == other.y + and self.a == other.a + and self.b == other.b + ) + + def __ne__(self, other): + # this should be the inverse of the == operator + return not (self == other) + + def __repr__(self): + if self.x is None: + return "Point(infinity)" + elif isinstance(self.x, FieldElement): + return "Point({},{})_{}_{} FieldElement({})".format( + self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime + ) + else: + return "Point({},{})_{}_{}".format(self.x, self.y, self.a, self.b) + + def __add__(self, other): + if self.a != other.a or self.b != other.b: + raise TypeError( + "Points {}, {} are not on the same curve".format(self, other) + ) + # Case 0.0: self is the point at infinity, return other + if self.x is None: + return other + # Case 0.1: other is the point at infinity, return self + if other.x is None: + return self + + # Case 1: self.x == other.x, self.y != other.y + # Result is point at infinity + if self.x == other.x and self.y != other.y: + return self.__class__(None, None, self.a, self.b) + + # Case 2: self.x ≠ other.x + # Formula (x3,y3)==(x1,y1)+(x2,y2) + # s=(y2-y1)/(x2-x1) + # x3=s**2-x1-x2 + # y3=s*(x1-x3)-y1 + if self.x != other.x: + s = (other.y - self.y) / (other.x - self.x) + x = s ** 2 - self.x - other.x + y = s * (self.x - x) - self.y + return self.__class__(x, y, self.a, self.b) + + # Case 4: if we are tangent to the vertical line, + # we return the point at infinity + # note instead of figuring out what 0 is for each type + # we just use 0 * self.x + if self == other and self.y == 0 * self.x: + return self.__class__(None, None, self.a, self.b) + + # Case 3: self == other + # Formula (x3,y3)=(x1,y1)+(x1,y1) + # s=(3*x1**2+a)/(2*y1) + # x3=s**2-2*x1 + # y3=s*(x1-x3)-y1 + if self == other: + s = (3 * self.x ** 2 + self.a) / (2 * self.y) + x = s ** 2 - 2 * self.x + y = s * (self.x - x) - self.y + return self.__class__(x, y, self.a, self.b) + + # tag::source3[] + def __rmul__(self, coefficient): + coef = coefficient + current = self # <1> + result = self.__class__(None, None, self.a, self.b) # <2> + while coef: + if coef & 1: # <3> + result += current + current += current # <4> + coef >>= 1 # <5> + return result diff --git a/Blockchain/Python_Package/EllepticCurve/Sha256Field.py b/Blockchain/Python_Package/EllepticCurve/Sha256Field.py new file mode 100644 index 0000000..be68718 --- /dev/null +++ b/Blockchain/Python_Package/EllepticCurve/Sha256Field.py @@ -0,0 +1,17 @@ +""" +Copyright (c) 2021 Codiesalert.com +These scripts should be used for commercial purpose without Codies Alert Permission +Any violations may lead to legal action +""" +from FieldElement import FieldElement +from Point import Point + +P = 2 ** 256 - 2 ** 32 - 977 + + +class Sha256Field(FieldElement): + def __init__(self, num, prime=None): + super().__init__(num=num, prime=P) + + def __repr__(self): + return "{:x}".format(self.num).zfill(64) diff --git a/Blockchain/Python_Package/EllepticCurve/__init__.py.txt b/Blockchain/Python_Package/EllepticCurve/__init__.py.txt new file mode 100644 index 0000000..e69de29 diff --git a/Blockchain/Python_Package/EllepticCurve/op.py b/Blockchain/Python_Package/EllepticCurve/op.py new file mode 100644 index 0000000..5956c58 --- /dev/null +++ b/Blockchain/Python_Package/EllepticCurve/op.py @@ -0,0 +1,79 @@ +""" +Copyright (c) 2021 Codiesalert.com +These scripts shouldn't be used for commercial purpose without Codies Alert Permission +Any violations may lead to legal action +""" +from Blockchain.Backend.util.util import hash160 +from Blockchain.Backend.core.EllepticCurve.EllepticCurve import Sha256Point, Signature + + +def op_dup(stack): + + if len(stack) < 1: + return False + stack.append(stack[-1]) + + return True + + +def op_hash160(stack): + if len(stack) < 1: + return False + element = stack.pop() + h160 = hash160(element) + stack.append(h160) + return True + + +def op_equal(stack): + if len(stack) < 2: + return False + + element1 = stack.pop() + element2 = stack.pop() + + if element1 == element2: + stack.append(1) + else: + stack.append(0) + + return True + + +def op_verify(stack): + if len(stack) < 1: + False + element = stack.pop() + + if element == 0: + return False + + return True + + +def op_equalverify(stack): + return op_equal(stack) and op_verify(stack) + + +def op_checksig(stack, z): + if len(stack) < 1: + return False + + sec_pubkey = stack.pop() + der_signature = stack.pop()[:-1] + + try: + point = Sha256Point.parse(sec_pubkey) + sig = Signature.parse(der_signature) + except Exception as e: + return False + + if point.verify(z, sig): + stack.append(1) + return True + else: + stack.append(0) + return False + + +OP_CODE_FUNCTION = {118: op_dup, 136: op_equalverify, 169: op_hash160, 172: op_checksig} diff --git a/Blockchain/Python_Package/EllepticCurve/test.py b/Blockchain/Python_Package/EllepticCurve/test.py new file mode 100644 index 0000000..3ce67f6 --- /dev/null +++ b/Blockchain/Python_Package/EllepticCurve/test.py @@ -0,0 +1,9 @@ +from FieldElement import FieldElement +from Point import Point +a = FieldElement(num = 0, prime = 223) +b = FieldElement(num = 7, prime = 223) +x = FieldElement(num = 192, prime = 223) +y = FieldElement(num = 105, prime = 223) + +p1 = Point(x, y, a, b) +print(p1) \ No newline at end of file diff --git a/Blockchain/Python_Package/requirements.txt b/Blockchain/Python_Package/requirements.txt new file mode 100644 index 0000000..828c394 --- /dev/null +++ b/Blockchain/Python_Package/requirements.txt @@ -0,0 +1,16 @@ +certifi +charset-normalizer +click +colorama +Flask +Flask-QRcode +idna +itsdangerous +Jinja2 +MarkupSafe +Pillow +pycryptodome +qrcode +requests +urllib3 +Werkzeug \ No newline at end of file diff --git a/Blockchain/Python_Package/test.py b/Blockchain/Python_Package/test.py new file mode 100644 index 0000000..f2de86f --- /dev/null +++ b/Blockchain/Python_Package/test.py @@ -0,0 +1,59 @@ +from flask import Flask, jsonify +import secrets +from EllepticCurve.EllepticCurve import Sha256Point +from util import hash160, hash256 + +app = Flask(__name__) + + +@app.route('/generate_keys', methods=['GET']) +def generate_keys(): + Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 + Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 + + G = Sha256Point(Gx, Gy) + + privateKey = secrets.randbits(256) + + unCompressedPublicKey = privateKey * G + xpoint = unCompressedPublicKey.x + ypoint = unCompressedPublicKey.y + + if ypoint.num % 2 == 0: + compressesKey = b"\x02" + xpoint.num.to_bytes(32, "big") + else: + compressesKey = b'\x03' + xpoint.num.to_bytes(32, 'big') + + hsh160 = hash160(compressesKey) + + main_prefix = b'\x00' + + newAddr = main_prefix + hsh160 + + """Checksum""" + checksum = hash256(newAddr)[:4] + newAddr = newAddr + checksum + BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' + + count = 0 + for c in newAddr: + if c == 0: + count += 1 + else: + break + + """convert address to numeric form""" + num = int.from_bytes(newAddr, 'big') + prefix = '1' * count + + result = '' + while num > 0: + num, mod = divmod(num, 58) + result = BASE58_ALPHABET[mod] + result + + PublicAddress = prefix + result + return {"privateKey": privateKey, "publicAddress": PublicAddress} + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/Blockchain/Python_Package/util.py b/Blockchain/Python_Package/util.py new file mode 100644 index 0000000..48739d1 --- /dev/null +++ b/Blockchain/Python_Package/util.py @@ -0,0 +1,12 @@ +import hashlib +from Crypto.Hash import RIPEMD160 +from hashlib import sha256 + + +def hash256(s): + """Two rounds of SHA256""" + return hashlib.sha256(hashlib.sha256(s).digest()).digest() + + +def hash160(s): + return RIPEMD160.new(sha256(s).digest()).digest() \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..88eebf8 --- /dev/null +++ b/composer.json @@ -0,0 +1,8 @@ +{ + "require": { + "kornrunner/secp256k1": "^0.2.0", + "phpseclib/phpseclib": "^3.0", + "mdanter/ecc": "^1.0", + "ext-curl": "*" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..efc439a --- /dev/null +++ b/composer.lock @@ -0,0 +1,448 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "28a0c5d4b11509df3024d5ca01ea7708", + "packages": [ + { + "name": "fgrosse/phpasn1", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/fgrosse/PHPASN1.git", + "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b", + "reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "~2.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "suggest": { + "ext-bcmath": "BCmath is the fallback extension for big integer calculations", + "ext-curl": "For loading OID information from the web if they have not bee defined statically", + "ext-gmp": "GMP is the preferred extension for big integer calculations", + "phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "FG\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Friedrich Große", + "email": "friedrich.grosse@gmail.com", + "homepage": "https://github.com/FGrosse", + "role": "Author" + }, + { + "name": "All contributors", + "homepage": "https://github.com/FGrosse/PHPASN1/contributors" + } + ], + "description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.", + "homepage": "https://github.com/FGrosse/PHPASN1", + "keywords": [ + "DER", + "asn.1", + "asn1", + "ber", + "binary", + "decoding", + "encoding", + "x.509", + "x.690", + "x509", + "x690" + ], + "support": { + "issues": "https://github.com/fgrosse/PHPASN1/issues", + "source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0" + }, + "abandoned": true, + "time": "2022-12-19T11:08:26+00:00" + }, + { + "name": "kornrunner/secp256k1", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/kornrunner/php-secp256k1.git", + "reference": "c3990dba47c7a8b0c9fd858fb29c61a5794fbb39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kornrunner/php-secp256k1/zipball/c3990dba47c7a8b0c9fd858fb29c61a5794fbb39", + "reference": "c3990dba47c7a8b0c9fd858fb29c61a5794fbb39", + "shasum": "" + }, + "require": { + "mdanter/ecc": "^1", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "kornrunner\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Boris Momčilović", + "email": "boris.momcilovic@gmail.com" + } + ], + "description": "Pure PHP secp256k1", + "keywords": [ + "curve", + "ecc", + "elliptic", + "secp256k1" + ], + "support": { + "issues": "https://github.com/kornrunner/php-secp256k1/issues", + "source": "https://github.com/kornrunner/php-secp256k1/tree/0.2.0" + }, + "time": "2021-01-19T03:30:01+00:00" + }, + { + "name": "mdanter/ecc", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/phpecc/phpecc.git", + "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpecc/phpecc/zipball/34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", + "reference": "34e2eec096bf3dcda814e8f66dd91ae87a2db7cd", + "shasum": "" + }, + "require": { + "ext-gmp": "*", + "fgrosse/phpasn1": "^2.0", + "php": "^7.0||^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0||^8.0||^9.0", + "squizlabs/php_codesniffer": "^2.0", + "symfony/yaml": "^2.6|^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mdanter\\Ecc\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matyas Danter", + "homepage": "http://matejdanter.com/", + "role": "Author" + }, + { + "name": "Thibaud Fabre", + "email": "thibaud@aztech.io", + "homepage": "http://aztech.io", + "role": "Maintainer" + }, + { + "name": "Thomas Kerin", + "email": "afk11@users.noreply.github.com", + "role": "Maintainer" + } + ], + "description": "PHP Elliptic Curve Cryptography library", + "homepage": "https://github.com/phpecc/phpecc", + "keywords": [ + "Diffie", + "ECDSA", + "Hellman", + "curve", + "ecdh", + "elliptic", + "nistp192", + "nistp224", + "nistp256", + "nistp384", + "nistp521", + "phpecc", + "secp256k1", + "secp256r1" + ], + "support": { + "issues": "https://github.com/phpecc/phpecc/issues", + "source": "https://github.com/phpecc/phpecc/tree/v1.0.0" + }, + "time": "2021-01-16T19:42:14+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.21", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2023-07-09T15:24:48+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/test.php b/test.php index 05a28a8..056abe5 100644 --- a/test.php +++ b/test.php @@ -1,14 +1,60 @@ lastBlock(); -if ($lastBlock) { - print_r($lastBlock); -} else { - echo "Blockchain is empty.\n"; + +require_once 'vendor/autoload.php'; + +use kornrunner\Secp256k1; +use kornrunner\Serializer\HexSignatureSerializer; + +class Account +{ + public function createKeys() + { + $secp256k1 = new Secp256k1(); + + // Generate a private key (random 256-bit integer) + $privateKey = random_bytes(32); + + // Generate the corresponding public key + $publicKey = $secp256k1->publicKeyCreate($privateKey); + + // Serialize the public key to its compressed format + $compressedPublicKey = $secp256k1->publicKeySerialize($publicKey); + + // Hash the compressed public key using RIPEMD160 + $hash160 = hash('ripemd160', hex2bin($compressedPublicKey)); + + // Add a prefix for the mainnet (0x00) + $mainnetPrefix = '00'; + $address = $mainnetPrefix . $hash160; + + // Double SHA-256 hash of the address, take the first 4 bytes as checksum + $checksum = substr(hash('sha256', hash('sha256', hex2bin($address), true)), 0, 8); + + // Append the checksum to the address + $addressWithChecksum = $address . $checksum; + + // Base58 encoding + $base58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + $count = 0; + for ($i = 0; $i < strlen($addressWithChecksum); $i++) { + if ($addressWithChecksum[$i] === '0') { + $count++; + } else { + break; + } + } + $num = gmp_init($addressWithChecksum, 16); + $base58Address = str_repeat('1', $count) . gmp_strval($num, 58, false); + + return [ + 'privateKey' => bin2hex($privateKey), + 'publicAddress' => $base58Address, + ]; + } } -echo "\n"; -print_r(__DIR__); -echo "\n"; -print_r(__FILE__); \ No newline at end of file + +$account = new Account(); +$keys = $account->createKeys(); + +echo "Private Key: " . $keys['privateKey'] . PHP_EOL; +echo "Public Address: " . $keys['publicAddress'] . PHP_EOL;
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: