Skip to content

Commit

Permalink
[ADD] Multiplication, addition, substraction of private keys
Browse files Browse the repository at this point in the history
  • Loading branch information
mccwdev committed Oct 31, 2023
1 parent ed3a9e4 commit 20594e8
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 20 deletions.
48 changes: 45 additions & 3 deletions bitcoinlib/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -930,10 +930,52 @@ def __bytes__(self):
return self.public_byte

def __add__(self, other):
return self.public_byte + other
"""
Scalar addition over secp256k1 order of 2 keys secrets. Returns a new private key with network and compressed
attributes from first key.
def __radd__(self, other):
return other + self.public_byte
:param other: Private Key class
:type other: Key
:return: Key
"""
assert self.is_private
assert isinstance(other, Key)
assert other.is_private
return Key((self.secret + other.secret) % secp256k1_n, self.network, self.compressed)

def __sub__(self, other):
"""
Scalar substraction over secp256k1 order of 2 keys secrets. Returns a new private key with network and
compressed attributes from first key.
:param other: Private Key class
:type other: Key
:return: Key
"""
assert self.is_private
assert isinstance(other, Key)
assert other.is_private
return Key((self.secret - other.secret) % secp256k1_n, self.network, self.compressed)

def __mul__(self, other):
"""
Scalar multiplication over secp256k1 order of 2 keys secrets. Returns a new private key with network and
compressed attributes from first key.
:param other: Private Key class
:type other: Key
:return: Key
"""
assert isinstance(other, Key)
assert self.secret
assert other.is_private
return Key((self.secret * other.secret) % secp256k1_n, self.network, self.compressed)

def __rmul__(self, other):
return self * other

def __len__(self):
return len(self.public_byte)
Expand Down
2 changes: 1 addition & 1 deletion bitcoinlib/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ def serialize(self):
if isinstance(cmd, int):
raw += bytes([cmd])
else:
raw += data_pack(cmd)
raw += data_pack(bytes(cmd))
self._raw = raw
return raw

Expand Down
47 changes: 32 additions & 15 deletions tests/test_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,38 @@
class TestKeyClasses(unittest.TestCase):

def test_keys_classes_dunder_methods(self):
pk = 'xprv9s21ZrQH143K4EDmQNMBqXwUTcrRoUctKkTegGsaBcMLnR1fJkMjVSRwVswjHzJspfWCUwzge1F521cY4wfWD54tzXVUqeo' \
'TFkZo17HiK2y'
k = HDKey(pk)
self.assertEqual(str(k), '03dc86716b2be27a0575558bac73279290ac22c3ea0240e42a2152d584f2b4006b')
self.assertEqual(len(k), 33)
self.assertEqual(int(k), 95796105828208927954168018443072630832764875640480247096632116413925408206516)
k2 = HDKey(pk)
self.assertTrue(k == k2)
pubk2 = HDKey(k.wif_public())
self.assertEqual(str(pubk2), '03dc86716b2be27a0575558bac73279290ac22c3ea0240e42a2152d584f2b4006b')
self.assertTrue(k.public() == pubk2)
self.assertEqual(k + k2, b'\x03\xdc\x86qk+\xe2z\x05uU\x8b\xacs\'\x92\x90\xac"\xc3\xea\x02@\xe4*!R\xd5'
b'\x84\xf2\xb4\x00k\x03\xdc\x86qk+\xe2z\x05uU\x8b\xacs\'\x92\x90\xac"'
b'\xc3\xea\x02@\xe4*!R\xd5\x84\xf2\xb4\x00k')
self.assertEqual(k + k2, k.public_byte + k2.public_byte)
secret_a = 91016841482436413813855602003356453732719866824300837492458390942862039054048
secret_b = 78671675202523181504169507283123166972338313435344626818080535590471773062636
secret_a_add_b = 53896427447643399894454124277791712852220615980570559927933763391815650622347
secret_a_min_b = 12345166279913232309686094720233286760381553388956210674377855352390265991412
ka = HDKey(secret_a)
ka2 = HDKey(secret_a)
kb = HDKey(secret_b)
self.assertEqual(str(ka), '02dff8866c7dc58055d9823dbc0ef098be76d8a1c87e545a13559460669b56a6a6')
self.assertEqual(len(ka), 33)
self.assertTrue(ka == ka2)
pub_ka = HDKey(ka.wif_public())
self.assertEqual(str(pub_ka), '02dff8866c7dc58055d9823dbc0ef098be76d8a1c87e545a13559460669b56a6a6')
self.assertTrue(ka.public() == pub_ka)
self.assertEqual((ka + kb).secret, secret_a_add_b)
self.assertEqual((kb + ka).secret, secret_a_add_b)
self.assertEqual((ka - kb).secret, secret_a_min_b)

def test_keys_classes_dunder_methods_mul(self):
secret_a = 101842203467542661703461476767681059717614296435193763347876672834253776929083
secret_b = 48056918761728599432510813046582785545807011954742048381717688544631745412510
secret_a_mul_b = 88863767166841201737805106153187292662619702602208852020796235484522800819015
ka = HDKey(secret_a)
kb = HDKey(secret_b)
self.assertEqual((ka * kb).secret, secret_a_mul_b)
self.assertEqual((kb * ka).secret, secret_a_mul_b)

def test_keys_proof_distributivity_of_scalar_operations(self):
# Proof: (a - b) * c == a * c - b * c over SECP256k1
ka = HDKey()
kb = HDKey()
kc = HDKey()
self.assertTrue(((ka - kb) * kc) == ((ka * kc) - (kb * kc)))

def test_dict_and_json_outputs(self):
k = HDKey()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -830,7 +830,7 @@ def test_script_create_redeemscript(self):
'00bd217870a8b4f1f09f3a8e8353ae'
self.assertEqual(expected_redeemscript, redeemscript.serialize().hex())

redeemscript3 = b'\x52' + b''.join([varstr(k) for k in keylist]) + b'\x53\xae'
redeemscript3 = b'\x52' + b''.join([varstr(k.public_byte) for k in keylist]) + b'\x53\xae'
self.assertEqual(redeemscript3, redeemscript.serialize())

def test_script_create_redeemscript_2(self):
Expand Down

0 comments on commit 20594e8

Please sign in to comment.