separate bitcoin related functions from wallet.py
authorthomasv <thomasv@gitorious>
Fri, 19 Oct 2012 12:55:01 +0000 (14:55 +0200)
committerthomasv <thomasv@gitorious>
Fri, 19 Oct 2012 12:55:01 +0000 (14:55 +0200)
lib/__init__.py
lib/bitcoin.py [new file with mode: 0644]
lib/util.py
lib/wallet.py
scripts/validate_tx
setup.py

index 845ae78..70fd32f 100644 (file)
@@ -1,3 +1,4 @@
 from wallet import Wallet, format_satoshis
 from interface import WalletSynchronizer, Interface, pick_random_server, DEFAULT_SERVERS
 from simple_config import SimpleConfig
+import bitcoin
diff --git a/lib/bitcoin.py b/lib/bitcoin.py
new file mode 100644 (file)
index 0000000..9cdb25b
--- /dev/null
@@ -0,0 +1,211 @@
+#!/usr/bin/env python
+#
+# Electrum - lightweight Bitcoin client
+# Copyright (C) 2011 thomasv@gitorious
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import hashlib, base64, ecdsa, re
+
+
+def rev_hex(s):
+    return s.decode('hex')[::-1].encode('hex')
+
+def int_to_hex(i, length=1):
+    s = hex(i)[2:].rstrip('L')
+    s = "0"*(2*length - len(s)) + s
+    return rev_hex(s)
+
+
+def Hash(data):
+    return hashlib.sha256(hashlib.sha256(data).digest()).digest()
+
+
+############ functions from pywallet ##################### 
+
+addrtype = 0
+
+def hash_160(public_key):
+    try:
+        md = hashlib.new('ripemd160')
+        md.update(hashlib.sha256(public_key).digest())
+        return md.digest()
+    except:
+        import ripemd
+        md = ripemd.new(hashlib.sha256(public_key).digest())
+        return md.digest()
+
+
+def public_key_to_bc_address(public_key):
+    h160 = hash_160(public_key)
+    return hash_160_to_bc_address(h160)
+
+def hash_160_to_bc_address(h160):
+    vh160 = chr(addrtype) + h160
+    h = Hash(vh160)
+    addr = vh160 + h[0:4]
+    return b58encode(addr)
+
+def bc_address_to_hash_160(addr):
+    bytes = b58decode(addr, 25)
+    return bytes[1:21]
+
+def encode_point(pubkey, compressed=False):
+    order = generator_secp256k1.order()
+    p = pubkey.pubkey.point
+    x_str = ecdsa.util.number_to_string(p.x(), order)
+    y_str = ecdsa.util.number_to_string(p.y(), order)
+    if compressed:
+        return chr(2 + (p.y() & 1)) + x_str
+    else:
+        return chr(4) + pubkey.to_string() #x_str + y_str
+
+__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
+__b58base = len(__b58chars)
+
+def b58encode(v):
+    """ encode v, which is a string of bytes, to base58."""
+
+    long_value = 0L
+    for (i, c) in enumerate(v[::-1]):
+        long_value += (256**i) * ord(c)
+
+    result = ''
+    while long_value >= __b58base:
+        div, mod = divmod(long_value, __b58base)
+        result = __b58chars[mod] + result
+        long_value = div
+    result = __b58chars[long_value] + result
+
+    # Bitcoin does a little leading-zero-compression:
+    # leading 0-bytes in the input become leading-1s
+    nPad = 0
+    for c in v:
+        if c == '\0': nPad += 1
+        else: break
+
+    return (__b58chars[0]*nPad) + result
+
+def b58decode(v, length):
+    """ decode v into a string of len bytes."""
+    long_value = 0L
+    for (i, c) in enumerate(v[::-1]):
+        long_value += __b58chars.find(c) * (__b58base**i)
+
+    result = ''
+    while long_value >= 256:
+        div, mod = divmod(long_value, 256)
+        result = chr(mod) + result
+        long_value = div
+    result = chr(long_value) + result
+
+    nPad = 0
+    for c in v:
+        if c == __b58chars[0]: nPad += 1
+        else: break
+
+    result = chr(0)*nPad + result
+    if length is not None and len(result) != length:
+        return None
+
+    return result
+
+
+def EncodeBase58Check(vchIn):
+    hash = Hash(vchIn)
+    return b58encode(vchIn + hash[0:4])
+
+def DecodeBase58Check(psz):
+    vchRet = b58decode(psz, None)
+    key = vchRet[0:-4]
+    csum = vchRet[-4:]
+    hash = Hash(key)
+    cs32 = hash[0:4]
+    if cs32 != csum:
+        return None
+    else:
+        return key
+
+def PrivKeyToSecret(privkey):
+    return privkey[9:9+32]
+
+def SecretToASecret(secret):
+    vchIn = chr(addrtype+128) + secret
+    return EncodeBase58Check(vchIn)
+
+def ASecretToSecret(key):
+    vch = DecodeBase58Check(key)
+    if vch and vch[0] == chr(addrtype+128):
+        return vch[1:]
+    else:
+        return False
+
+########### end pywallet functions #######################
+
+# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
+_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
+_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
+_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
+_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
+_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
+_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
+curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
+generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
+oid_secp256k1 = (1,3,132,0,10)
+SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 ) 
+
+
+def filter(s): 
+    out = re.sub('( [^\n]*|)\n','',s)
+    out = out.replace(' ','')
+    out = out.replace('\n','')
+    return out
+
+def raw_tx( inputs, outputs, for_sig = None ):
+    s  = int_to_hex(1,4)                                     +   '     version\n' 
+    s += int_to_hex( len(inputs) )                           +   '     number of inputs\n'
+    for i in range(len(inputs)):
+        _, _, p_hash, p_index, p_script, pubkey, sig = inputs[i]
+        s += p_hash.decode('hex')[::-1].encode('hex')        +  '     prev hash\n'
+        s += int_to_hex(p_index,4)                           +  '     prev index\n'
+        if for_sig is None:
+            sig = sig + chr(1)                               # hashtype
+            script  = int_to_hex( len(sig))                  +  '     push %d bytes\n'%len(sig)
+            script += sig.encode('hex')                      +  '     sig\n'
+            pubkey = chr(4) + pubkey
+            script += int_to_hex( len(pubkey))               +  '     push %d bytes\n'%len(pubkey)
+            script += pubkey.encode('hex')                   +  '     pubkey\n'
+        elif for_sig==i:
+            script = p_script                                +  '     scriptsig \n'
+        else:
+            script=''
+        s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
+        s += script
+        s += "ffffffff"                                      +  '     sequence\n'
+    s += int_to_hex( len(outputs) )                          +  '     number of outputs\n'
+    for output in outputs:
+        addr, amount = output
+        s += int_to_hex( amount, 8)                          +  '     amount: %d\n'%amount 
+        script = '76a9'                                      # op_dup, op_hash_160
+        script += '14'                                       # push 0x14 bytes
+        script += bc_address_to_hash_160(addr).encode('hex')
+        script += '88ac'                                     # op_equalverify, op_checksig
+        s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
+        s += script                                          +  '     script \n'
+    s += int_to_hex(0,4)                                     # lock time
+    if for_sig is not None: s += int_to_hex(1, 4)            # hash type
+    return s
+
+
index 28a3ffd..ae5380b 100644 (file)
@@ -1,6 +1,5 @@
-import os
+import os, sys
 import platform
-import sys
 
 def print_error(*args):
     # Stringify args
@@ -8,6 +7,7 @@ def print_error(*args):
     sys.stderr.write(" ".join(args) + "\n")
     sys.stderr.flush()
 
+
 def user_dir():
     if "HOME" in os.environ:
       return os.path.join(os.environ["HOME"], ".electrum")
@@ -18,6 +18,7 @@ def user_dir():
     else:
       raise BaseException("No home directory found in environment variables.")
 
+
 def appdata_dir():
     """Find the path to the application data directory; add an electrum folder and return path."""
     if platform.system() == "Windows":
@@ -30,9 +31,11 @@ def appdata_dir():
     else:
         raise Exception("Unknown system")
 
+
 def get_resource_path(*args):
     return os.path.join(".", *args)
 
+
 def local_data_dir():
     """Return path to the data folder."""
     assert sys.argv
@@ -40,3 +43,23 @@ def local_data_dir():
     local_data = os.path.join(prefix_path, "data")
     return local_data
 
+
+def format_satoshis(x, is_diff=False, num_zeros = 0):
+    from decimal import Decimal
+    s = Decimal(x)
+    sign, digits, exp = s.as_tuple()
+    digits = map(str, digits)
+    while len(digits) < 9:
+        digits.insert(0,'0')
+    digits.insert(-8,'.')
+    s = ''.join(digits).rstrip('0')
+    if sign: 
+        s = '-' + s
+    elif is_diff:
+        s = "+" + s
+
+    p = s.find('.')
+    s += "0"*( 1 + num_zeros - ( len(s) - p ))
+    s += " "*( 9 - ( len(s) - p ))
+    s = " "*( 5 - ( p )) + s
+    return s
index 72e1672..2e5cc78 100644 (file)
@@ -30,233 +30,21 @@ import aes
 import ecdsa
 
 from ecdsa.util import string_to_number, number_to_string
-from util import print_error
-from util import user_dir
-
-############ functions from pywallet ##################### 
-
-addrtype = 0
-
-def hash_160(public_key):
-    try:
-        md = hashlib.new('ripemd160')
-        md.update(hashlib.sha256(public_key).digest())
-        return md.digest()
-    except:
-        import ripemd
-        md = ripemd.new(hashlib.sha256(public_key).digest())
-        return md.digest()
-
-
-def public_key_to_bc_address(public_key):
-    h160 = hash_160(public_key)
-    return hash_160_to_bc_address(h160)
-
-def hash_160_to_bc_address(h160):
-    vh160 = chr(addrtype) + h160
-    h = Hash(vh160)
-    addr = vh160 + h[0:4]
-    return b58encode(addr)
-
-def bc_address_to_hash_160(addr):
-    bytes = b58decode(addr, 25)
-    return bytes[1:21]
-
-def encode_point(pubkey, compressed=False):
-    order = generator_secp256k1.order()
-    p = pubkey.pubkey.point
-    x_str = ecdsa.util.number_to_string(p.x(), order)
-    y_str = ecdsa.util.number_to_string(p.y(), order)
-    if compressed:
-        return chr(2 + (p.y() & 1)) + x_str
-    else:
-        return chr(4) + pubkey.to_string() #x_str + y_str
-
-__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
-__b58base = len(__b58chars)
-
-def b58encode(v):
-    """ encode v, which is a string of bytes, to base58."""
-
-    long_value = 0L
-    for (i, c) in enumerate(v[::-1]):
-        long_value += (256**i) * ord(c)
-
-    result = ''
-    while long_value >= __b58base:
-        div, mod = divmod(long_value, __b58base)
-        result = __b58chars[mod] + result
-        long_value = div
-    result = __b58chars[long_value] + result
-
-    # Bitcoin does a little leading-zero-compression:
-    # leading 0-bytes in the input become leading-1s
-    nPad = 0
-    for c in v:
-        if c == '\0': nPad += 1
-        else: break
-
-    return (__b58chars[0]*nPad) + result
-
-def b58decode(v, length):
-    """ decode v into a string of len bytes."""
-    long_value = 0L
-    for (i, c) in enumerate(v[::-1]):
-        long_value += __b58chars.find(c) * (__b58base**i)
-
-    result = ''
-    while long_value >= 256:
-        div, mod = divmod(long_value, 256)
-        result = chr(mod) + result
-        long_value = div
-    result = chr(long_value) + result
-
-    nPad = 0
-    for c in v:
-        if c == __b58chars[0]: nPad += 1
-        else: break
-
-    result = chr(0)*nPad + result
-    if length is not None and len(result) != length:
-        return None
-
-    return result
-
-
-def Hash(data):
-    return hashlib.sha256(hashlib.sha256(data).digest()).digest()
-
-def EncodeBase58Check(vchIn):
-    hash = Hash(vchIn)
-    return b58encode(vchIn + hash[0:4])
-
-def DecodeBase58Check(psz):
-    vchRet = b58decode(psz, None)
-    key = vchRet[0:-4]
-    csum = vchRet[-4:]
-    hash = Hash(key)
-    cs32 = hash[0:4]
-    if cs32 != csum:
-        return None
-    else:
-        return key
-
-def PrivKeyToSecret(privkey):
-    return privkey[9:9+32]
-
-def SecretToASecret(secret):
-    vchIn = chr(addrtype+128) + secret
-    return EncodeBase58Check(vchIn)
-
-def ASecretToSecret(key):
-    vch = DecodeBase58Check(key)
-    if vch and vch[0] == chr(addrtype+128):
-        return vch[1:]
-    else:
-        return False
-
-########### end pywallet functions #######################
-
+from util import print_error, user_dir, format_satoshis
+from bitcoin import *
 
 # URL decode
 _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE)
 urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x)
 
-
-def int_to_hex(i, length=1):
-    s = hex(i)[2:].rstrip('L')
-    s = "0"*(2*length - len(s)) + s
-    return s.decode('hex')[::-1].encode('hex')
-
-
-# AES
+# AES encryption
 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
 
 
-
-# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
-_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
-_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
-_b = 0x0000000000000000000000000000000000000000000000000000000000000007L
-_a = 0x0000000000000000000000000000000000000000000000000000000000000000L
-_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
-_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
-curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
-generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
-oid_secp256k1 = (1,3,132,0,10)
-SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 ) 
-
-
-def filter(s): 
-    out = re.sub('( [^\n]*|)\n','',s)
-    out = out.replace(' ','')
-    out = out.replace('\n','')
-    return out
-
-def raw_tx( inputs, outputs, for_sig = None ):
-    s  = int_to_hex(1,4)                                     +   '     version\n' 
-    s += int_to_hex( len(inputs) )                           +   '     number of inputs\n'
-    for i in range(len(inputs)):
-        _, _, p_hash, p_index, p_script, pubkey, sig = inputs[i]
-        s += p_hash.decode('hex')[::-1].encode('hex')        +  '     prev hash\n'
-        s += int_to_hex(p_index,4)                           +  '     prev index\n'
-        if for_sig is None:
-            sig = sig + chr(1)                               # hashtype
-            script  = int_to_hex( len(sig))                  +  '     push %d bytes\n'%len(sig)
-            script += sig.encode('hex')                      +  '     sig\n'
-            pubkey = chr(4) + pubkey
-            script += int_to_hex( len(pubkey))               +  '     push %d bytes\n'%len(pubkey)
-            script += pubkey.encode('hex')                   +  '     pubkey\n'
-        elif for_sig==i:
-            script = p_script                                +  '     scriptsig \n'
-        else:
-            script=''
-        s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
-        s += script
-        s += "ffffffff"                                      +  '     sequence\n'
-    s += int_to_hex( len(outputs) )                          +  '     number of outputs\n'
-    for output in outputs:
-        addr, amount = output
-        s += int_to_hex( amount, 8)                          +  '     amount: %d\n'%amount 
-        script = '76a9'                                      # op_dup, op_hash_160
-        script += '14'                                       # push 0x14 bytes
-        script += bc_address_to_hash_160(addr).encode('hex')
-        script += '88ac'                                     # op_equalverify, op_checksig
-        s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
-        s += script                                          +  '     script \n'
-    s += int_to_hex(0,4)                                     # lock time
-    if for_sig is not None: s += int_to_hex(1, 4)            # hash type
-    return s
-
-
-
-
-def format_satoshis(x, is_diff=False, num_zeros = 0):
-    from decimal import Decimal
-    s = Decimal(x)
-    sign, digits, exp = s.as_tuple()
-    digits = map(str, digits)
-    while len(digits) < 9:
-        digits.insert(0,'0')
-    digits.insert(-8,'.')
-    s = ''.join(digits).rstrip('0')
-    if sign: 
-        s = '-' + s
-    elif is_diff:
-        s = "+" + s
-
-    p = s.find('.')
-    s += "0"*( 1 + num_zeros - ( len(s) - p ))
-    s += " "*( 9 - ( len(s) - p ))
-    s = " "*( 5 - ( p )) + s
-    return s
-
-
 from version import ELECTRUM_VERSION, SEED_VERSION
 
 
-
 class Wallet:
     def __init__(self, config={}):
 
@@ -285,7 +73,6 @@ class Wallet:
         self.addressbook           = config.get('contacts', [])           # outgoing addresses, for payments
         self.imported_keys         = config.get('imported_keys',{})
 
-
         # not saved
         self.receipt = None          # next receipt
         self.tx_history = {}
index 153b4c9..6671fe9 100755 (executable)
@@ -2,21 +2,11 @@
 
 import sys, hashlib
 from electrum import Interface
+from electrum.bitcoin import Hash, rev_hex, int_to_hex
 
 """validate a transaction (SPV)"""
 
 
-Hash = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest()
-
-def rev_hex(s):
-    return s.decode('hex')[::-1].encode('hex')
-
-def int_to_hex(i, length=1):
-    s = hex(i)[2:].rstrip('L')
-    s = "0"*(2*length - len(s)) + s
-    return rev_hex(s)
-
-
 i = Interface({'server':'ecdsa.org:50002:s'})
 i.start()
 
index dda0128..f9f1332 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -59,6 +59,7 @@ setup(name = "Electrum",
                   'electrum.bmp',
                   'electrum.msqr',
                   'electrum.util',
+                  'electrum.bitcoin',
                   'electrum.i18n'],
     description = "Lightweight Bitcoin Wallet",
     author = "thomasv",