Created
January 24, 2025 00:00
-
-
Save Goatghosts/672bbace3dd8f13c60c46cc95fd1aa09 to your computer and use it in GitHub Desktop.
This script generates x-only Schnorr keys on the secp256k1 curve, constructs Kaspa addresses from them, and verifies their correctness through reverse decoding.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PREFIX = "kaspa" # kaspa/kaspatest | |
VERSION = 0 # PubKey = 0 | PubKeyECDSA = 1 | ScriptHash = 8 | |
CHARSET = b"qpzry9x8gf2tvdw0s3jn54khce6mua7l" | |
REV_CHARSET = {c: i for i, c in enumerate(CHARSET)} | |
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F | |
A = 0x0000000000000000000000000000000000000000000000000000000000000000 | |
B = 0x0000000000000000000000000000000000000000000000000000000000000007 | |
G = [ | |
55066263022277343669578718895168534326250603453777594175500187360389116729240, | |
32670510020758816978083085130507043184471273380659243275938904335757337482424, | |
] | |
def scalar_mult(scalar, point): | |
current_point = point.copy() | |
bits = bin(scalar)[2:] | |
for bit in bits[1:]: | |
current_point = double_point(current_point) | |
if bit == "1": | |
current_point = add_points(current_point, point) | |
return current_point | |
def double_point(point): | |
x, y = point | |
s = ((3 * x * x + A) * pow(2 * y, P - 2, P)) % P | |
x3 = (s * s - 2 * x) % P | |
y3 = (s * (x - x3) - y) % P | |
return (x3, y3) | |
def add_points(point1, point2): | |
if point1 == point2: | |
return double_point(point1) | |
x1, y1 = point1 | |
x2, y2 = point2 | |
s = ((y2 - y1) * pow(x2 - x1, P - 2, P)) % P | |
x3 = (s * s - x1 - x2) % P | |
y3 = (s * (x1 - x3) - y1) % P | |
return (x3, y3) | |
def polymod(values): | |
c = 1 | |
for d in values: | |
c0 = c >> 35 | |
c = ((c & 0x07FFFFFFFF) << 5) ^ d | |
if c0 & 1: | |
c ^= 0x98F2BC8E61 | |
if c0 & 2: | |
c ^= 0x79B76D99E2 | |
if c0 & 4: | |
c ^= 0xF33E5FB3C4 | |
if c0 & 8: | |
c ^= 0xAE2EABE2A8 | |
if c0 & 16: | |
c ^= 0x1E4F43E470 | |
return c ^ 1 | |
def checksum(payload, prefix): | |
arr = [(x & 0x1F) for x in prefix.encode()] | |
arr.append(0) | |
arr.extend(payload) | |
arr.extend([0] * 8) | |
return polymod(arr) | |
def conv8to5(data): | |
bits = 0 | |
current = 0 | |
r = [] | |
for c in data: | |
current = (current << 8) | c | |
bits += 8 | |
while bits >= 5: | |
bits -= 5 | |
r.append((current >> bits) & 31) | |
if bits > 0: | |
r.append((current << (5 - bits)) & 31) | |
return r | |
def conv5to8(data): | |
bits = 0 | |
current = 0 | |
r = [] | |
for c in data: | |
current = (current << 5) | c | |
bits += 5 | |
while bits >= 8: | |
bits -= 8 | |
r.append((current >> bits) & 255) | |
return bytes(r) | |
def encode_address(prefix, version, payload): | |
r = conv8to5(bytes([version]) + payload) | |
c = checksum(r, prefix) | |
c &= 0xFFFFFFFFFF | |
chk = conv8to5(c.to_bytes(5, "big")) | |
return "".join(chr(CHARSET[i]) for i in (r + chk)) | |
def decode_address(prefix, address): | |
b = [] | |
for ch in address: | |
val = REV_CHARSET.get(ch.encode()[0], 100) | |
if val == 100: | |
raise Exception(f"Decoding error at character '{ch}'") | |
b.append(val) | |
if len(b) < 8: | |
raise Exception("Bad payload") | |
p, c = b[:-8], b[-8:] | |
cc = int.from_bytes(conv5to8(c), "big") | |
if cc != checksum(p, prefix): | |
raise Exception("Bad checksum") | |
u = conv5to8(p) | |
if len(u) < 2: | |
raise Exception("Bad payload") | |
return u[0], u[1:] | |
def xpoint_to_scriptPubKey(xpoint_bytes): | |
return b"\x00\x00\x20" + xpoint_bytes + b"\xac" | |
def main(): | |
secret = 40500595084585002008095440525403209228189253202440630095320916245962672666978 | |
print(f'Secret Key: {secret.to_bytes(32, "big").hex()}') | |
x, y = scalar_mult(secret, G) | |
x_point = x.to_bytes(32, "big") | |
print(f"Public X: {x_point.hex()}") | |
print(f'Public Y: {y.to_bytes(32, "big").hex()}') | |
scriptPublicKey = xpoint_to_scriptPubKey(x_point).hex() | |
print(f"scriptPublicKey: {scriptPublicKey}") | |
encoded_address = encode_address(PREFIX, VERSION, x_point) | |
print(f"Address: {PREFIX}:{encoded_address}") | |
recovered_version, recovered_x_point = decode_address(PREFIX, encoded_address) | |
print(f"Recovered version: {recovered_version}") | |
print(f"Recovered x-point: {recovered_x_point.hex()}") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment