import hashlib, base64, ecdsa, re
-
+from util import print_error
def rev_hex(s):
return s.decode('hex')[::-1].encode('hex')
else:
return "ff"+int_to_hex(i,8)
+def op_push(i):
+ if i<0x4c:
+ return int_to_hex(i)
+ elif i<0xff:
+ return '4c' + int_to_hex(i)
+ elif i<0xffff:
+ return '4d' + int_to_hex(i,2)
+ else:
+ return '4e' + int_to_hex(i,4)
+
+
Hash = lambda x: hashlib.sha256(hashlib.sha256(x).digest()).digest()
hash_encode = lambda x: x[::-1].encode('hex')
b = ASecretToSecret(sec)
return len(b) == 33
+
+def address_from_private_key(sec):
+ # rebuild public key from private key, compressed or uncompressed
+ pkey = regenerate_key(sec)
+ assert pkey
+
+ # figure out if private key is compressed
+ compressed = is_compressed(sec)
+
+ # rebuild private and public key from regenerated secret
+ private_key = GetPrivKey(pkey, compressed)
+ public_key = GetPubKey(pkey.pubkey, compressed)
+ address = public_key_to_bc_address(public_key)
+ return address
+
+
########### end pywallet functions #######################
# secp256k1, http://www.oid-info.com/get/1.3.132.0.10
oid_secp256k1 = (1,3,132,0,10)
SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 )
+from ecdsa.util import string_to_number, number_to_string
+
+def msg_magic(message):
+ return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message
+
+
class EC_KEY(object):
def __init__( self, secret ):
self.pubkey = ecdsa.ecdsa.Public_key( generator_secp256k1, generator_secp256k1 * secret )
self.privkey = ecdsa.ecdsa.Private_key( self.pubkey, secret )
self.secret = secret
+ def sign_message(self, message, compressed, address):
+ private_key = ecdsa.SigningKey.from_secret_exponent( self.secret, curve = SECP256k1 )
+ public_key = private_key.get_verifying_key()
+ signature = private_key.sign_digest( Hash( msg_magic(message) ), sigencode = ecdsa.util.sigencode_string )
+ assert public_key.verify_digest( signature, Hash( msg_magic(message) ), sigdecode = ecdsa.util.sigdecode_string)
+ for i in range(4):
+ sig = base64.b64encode( chr(27 + i + (4 if compressed else 0)) + signature )
+ try:
+ self.verify_message( address, sig, message)
+ return sig
+ except:
+ continue
+ else:
+ raise BaseException("error: cannot sign message")
+
+ @classmethod
+ def verify_message(self, address, signature, message):
+ """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """
+ from ecdsa import numbertheory, ellipticcurve, util
+ import msqr
+ curve = curve_secp256k1
+ G = generator_secp256k1
+ order = G.order()
+ # extract r,s from signature
+ sig = base64.b64decode(signature)
+ if len(sig) != 65: raise BaseException("Wrong encoding")
+ r,s = util.sigdecode_string(sig[1:], order)
+ nV = ord(sig[0])
+ if nV < 27 or nV >= 35:
+ raise BaseException("Bad encoding")
+ if nV >= 31:
+ compressed = True
+ nV -= 4
+ else:
+ compressed = False
+
+ recid = nV - 27
+ # 1.1
+ x = r + (recid/2) * order
+ # 1.3
+ alpha = ( x * x * x + curve.a() * x + curve.b() ) % curve.p()
+ beta = msqr.modular_sqrt(alpha, curve.p())
+ y = beta if (beta - recid) % 2 == 0 else curve.p() - beta
+ # 1.4 the constructor checks that nR is at infinity
+ R = ellipticcurve.Point(curve, x, y, order)
+ # 1.5 compute e from message:
+ h = Hash( msg_magic(message) )
+ e = string_to_number(h)
+ minus_e = -e % order
+ # 1.6 compute Q = r^-1 (sR - eG)
+ inv_r = numbertheory.inverse_mod(r,order)
+ Q = inv_r * ( s * R + minus_e * G )
+ public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 )
+ # check that Q is the public key
+ public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
+ # check that we get the original signing address
+ addr = public_key_to_bc_address( encode_point(public_key, compressed) )
+ if address != addr:
+ raise BaseException("Bad signature")
+
###################################### BIP32 ##############################
+random_seed = lambda n: "%032x"%ecdsa.util.randrange( pow(2,n) )
+
+
+
def bip32_init(seed):
import hmac
+class DeterministicSequence:
+ """ Privatekey(type,n) = Master_private_key + H(n|S|type) """
+ def __init__(self, master_public_key):
+ self.master_public_key = master_public_key
-################################## transactions
-
+ @classmethod
+ def from_seed(klass, seed):
+ curve = SECP256k1
+ secexp = klass.stretch_key(seed)
+ master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
+ master_public_key = master_private_key.get_verifying_key().to_string().encode('hex')
+ self = klass(master_public_key)
+ return self
-def tx_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
- s += var_int( len(inputs) ) # number of inputs
- for i in range(len(inputs)):
- txin = inputs[i]
- pubkeysig = txin.get('pubkeysig',[])
- s += txin['tx_hash'].decode('hex')[::-1].encode('hex') # prev hash
- s += int_to_hex(txin['index'],4) # prev index
-
- if for_sig is None:
- if len(pubkeysig) == 1:
- pubkey, sig = pubkeysig[0]
- sig = sig + chr(1) # hashtype
- script = int_to_hex( len(sig))
- script += sig.encode('hex')
- script += int_to_hex( len(pubkey))
- script += pubkey.encode('hex')
- else:
- pubkey0, sig0 = pubkeysig[0]
- pubkey1, sig1 = pubkeysig[1]
- sig0 = sig0 + chr(1)
- sig1 = sig1 + chr(1)
- inner_script = multisig_script([pubkey0, pubkey1])
- script = '00' # op_0
- script += int_to_hex(len(sig0))
- script += sig0.encode('hex')
- script += int_to_hex(len(sig1))
- script += sig1.encode('hex')
- script += var_int(len(inner_script)/2)
- script += inner_script
-
- elif for_sig==i:
- if len(pubkeysig) > 1:
- script = multisig_script(pubkeysig) # p2sh uses the inner script
- else:
- script = txin['raw_output_script'] # scriptsig
- else:
- script=''
- s += var_int( len(tx_filter(script))/2 ) # script length
- s += script
- s += "ffffffff" # sequence
-
- s += var_int( len(outputs) ) # number of outputs
- for output in outputs:
- addr, amount = output
- s += int_to_hex( amount, 8) # amount
- addrtype, hash_160 = bc_address_to_hash_160(addr)
- if addrtype == 0:
- script = '76a9' # op_dup, op_hash_160
- script += '14' # push 0x14 bytes
- script += hash_160.encode('hex')
- script += '88ac' # op_equalverify, op_checksig
- elif addrtype == 5:
- script = 'a9' # op_hash_160
- script += '14' # push 0x14 bytes
- script += hash_160.encode('hex')
- script += '87' # op_equal
- else:
- raise
-
- s += var_int( len(tx_filter(script))/2 ) # script length
- s += script # script
- s += int_to_hex(0,4) # lock time
- if for_sig is not None and for_sig != 1: s += int_to_hex(1, 4) # hash type
- return tx_filter(s)
+ @classmethod
+ def stretch_key(self,seed):
+ oldseed = seed
+ for i in range(100000):
+ seed = hashlib.sha256(seed + oldseed).digest()
+ return string_to_number( seed )
+
+ def get_sequence(self,n,for_change):
+ return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key.decode('hex') ) )
+
+ def get_pubkey(self, n, for_change):
+ curve = SECP256k1
+ z = self.get_sequence(n, for_change)
+ master_public_key = ecdsa.VerifyingKey.from_string( self.master_public_key.decode('hex'), curve = SECP256k1 )
+ pubkey_point = master_public_key.pubkey.point + z*curve.generator
+ public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 )
+ return '04' + public_key2.to_string().encode('hex')
+
+ def get_private_key(self, n, for_change, seed):
+ order = generator_secp256k1.order()
+ secexp = self.stretch_key(seed)
+ secexp = ( secexp + self.get_sequence(n,for_change) ) % order
+ pk = number_to_string( secexp, generator_secp256k1.order() )
+ compressed = False
+ return SecretToASecret( pk, compressed )
+
+ def check_seed(self, seed):
+ curve = SECP256k1
+ secexp = self.stretch_key(seed)
+ master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
+ master_public_key = master_private_key.get_verifying_key().to_string().encode('hex')
+ if master_public_key != self.master_public_key:
+ print_error('invalid password (mpk)')
+ raise BaseException('Invalid password')
+
+ return True
+################################## transactions
-def multisig_script(public_keys, num=None):
- # supports only "2 of 2", and "2 of 3" transactions
- n = len(public_keys)
- if num is None:
- num = n
- assert num <= n and n <= 3 and n >= 2
-
- if num==2:
- s = '52'
- elif num == 3:
- s = '53'
- else:
- raise
-
- for k in public_keys:
- s += var_int(len(k)/2)
- s += k
- if n==2:
- s += '52'
- elif n==3:
- s += '53'
- else:
- raise
- s += 'ae'
- return s
@classmethod
def from_io(klass, inputs, outputs):
- raw = raw_tx(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
+ raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
self = klass(raw)
+ self.is_complete = False
self.inputs = inputs
self.outputs = outputs
+ extras = []
+ for i in self.inputs:
+ e = { 'txid':i['tx_hash'], 'vout':i['index'],'scriptPubKey':i['raw_output_script'] }
+ extras.append(e)
+ self.input_info = extras
return self
def __str__(self):
return self.raw
+ @classmethod
+ def multisig_script(klass, public_keys, num=None):
+ n = len(public_keys)
+ if num is None: num = n
+ # supports only "2 of 2", and "2 of 3" transactions
+ assert num <= n and n in [2,3]
+
+ if num==2:
+ s = '52'
+ elif num == 3:
+ s = '53'
+ else:
+ raise
+
+ for k in public_keys:
+ s += var_int(len(k)/2)
+ s += k
+ if n==2:
+ s += '52'
+ elif n==3:
+ s += '53'
+ else:
+ raise
+ s += 'ae'
+
+ out = { "address": hash_160_to_bc_address(hash_160(s.decode('hex')), 5), "redeemScript":s }
+ return out
+
+ @classmethod
+ def serialize( klass, inputs, outputs, for_sig = None ):
+
+ s = int_to_hex(1,4) # version
+ s += var_int( len(inputs) ) # number of inputs
+ for i in range(len(inputs)):
+ txin = inputs[i]
+ s += txin['tx_hash'].decode('hex')[::-1].encode('hex') # prev hash
+ s += int_to_hex(txin['index'],4) # prev index
+
+ if for_sig is None:
+ pubkeysig = txin.get('pubkeysig')
+ if pubkeysig:
+ pubkey, sig = pubkeysig[0]
+ sig = sig + chr(1) # hashtype
+ script = op_push( len(sig))
+ script += sig.encode('hex')
+ script += op_push( len(pubkey))
+ script += pubkey.encode('hex')
+ else:
+ signatures = txin['signatures']
+ pubkeys = txin['pubkeys']
+ script = '00' # op_0
+ for sig in signatures:
+ sig = sig + '01'
+ script += op_push(len(sig)/2)
+ script += sig
+
+ redeem_script = klass.multisig_script(pubkeys,2).get('redeemScript')
+ script += op_push(len(redeem_script)/2)
+ script += redeem_script
+
+ elif for_sig==i:
+ if txin.get('redeemScript'):
+ script = txin['redeemScript'] # p2sh uses the inner script
+ else:
+ script = txin['raw_output_script'] # scriptsig
+ else:
+ script=''
+ s += var_int( len(script)/2 ) # script length
+ s += script
+ s += "ffffffff" # sequence
+
+ s += var_int( len(outputs) ) # number of outputs
+ for output in outputs:
+ addr, amount = output
+ s += int_to_hex( amount, 8) # amount
+ addrtype, hash_160 = bc_address_to_hash_160(addr)
+ if addrtype == 0:
+ script = '76a9' # op_dup, op_hash_160
+ script += '14' # push 0x14 bytes
+ script += hash_160.encode('hex')
+ script += '88ac' # op_equalverify, op_checksig
+ elif addrtype == 5:
+ script = 'a9' # op_hash_160
+ script += '14' # push 0x14 bytes
+ script += hash_160.encode('hex')
+ script += '87' # op_equal
+ else:
+ raise
+
+ s += var_int( len(script)/2 ) # script length
+ s += script # script
+ s += int_to_hex(0,4) # lock time
+ if for_sig is not None and for_sig != -1:
+ s += int_to_hex(1, 4) # hash type
+ return s
+
+
+ def for_sig(self,i):
+ return self.serialize(self.inputs, self.outputs, for_sig = i)
+
+
def hash(self):
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
def sign(self, private_keys):
+ import deserialize
for i in range(len(self.inputs)):
txin = self.inputs[i]
- secexp, compressed = private_keys[txin['address']]
- private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
- public_key = private_key.get_verifying_key()
- pkey = EC_KEY(secexp)
- pubkey = GetPubKey(pkey.pubkey, compressed)
- tx = raw_tx( self.inputs, self.outputs, for_sig = i )
- sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
- assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
- self.inputs[i]["pubkeysig"] = [(pubkey, sig)]
+ tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i )
+
+ if txin.get('redeemScript'):
+ # 1 parse the redeem script
+ num, redeem_pubkeys = deserialize.parse_redeemScript(txin.get('redeemScript'))
+ self.inputs[i]["pubkeys"] = redeem_pubkeys
+
+ # build list of public/private keys
+ keypairs = {}
+ for sec in private_keys.values():
+ compressed = is_compressed(sec)
+ pkey = regenerate_key(sec)
+ pubkey = GetPubKey(pkey.pubkey, compressed)
+ keypairs[ pubkey.encode('hex') ] = sec
+
+ # list of already existing signatures
+ signatures = txin.get("signatures",[])
+ print_error("signatures",signatures)
+
+ for pubkey in redeem_pubkeys:
+ public_key = ecdsa.VerifyingKey.from_string(pubkey[2:].decode('hex'), curve = SECP256k1)
+ for s in signatures:
+ try:
+ public_key.verify_digest( s.decode('hex')[:-1], Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
+ break
+ except ecdsa.keys.BadSignatureError:
+ continue
+ else:
+ # check if we have a key corresponding to the redeem script
+ if pubkey in keypairs.keys():
+ # add signature
+ sec = keypairs[pubkey]
+ compressed = is_compressed(sec)
+ pkey = regenerate_key(sec)
+ secexp = pkey.secret
+ private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
+ public_key = private_key.get_verifying_key()
+ sig = private_key.sign_digest( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
+ assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
+ signatures.append( sig.encode('hex') )
+
+ # for p2sh, pubkeysig is a tuple (may be incomplete)
+ self.inputs[i]["signatures"] = signatures
+ print_error("signatures",signatures)
+ self.is_complete = len(signatures) == num
+
+ else:
+ sec = private_keys[txin['address']]
+ compressed = is_compressed(sec)
+ pkey = regenerate_key(sec)
+ secexp = pkey.secret
- self.raw = raw_tx( self.inputs, self.outputs )
+ private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
+ public_key = private_key.get_verifying_key()
+ pkey = EC_KEY(secexp)
+ pubkey = GetPubKey(pkey.pubkey, compressed)
+ sig = private_key.sign_digest( Hash( tx_for_sig.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
+ assert public_key.verify_digest( sig, Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
+
+ self.inputs[i]["pubkeysig"] = [(pubkey, sig)]
+ self.is_complete = True
+
+ self.raw = self.serialize( self.inputs, self.outputs )
def deserialize(self):
return self.d
+ def has_address(self, addr):
+ found = False
+ for txin in self.inputs:
+ if addr == txin.get('address'):
+ found = True
+ break
+ for txout in self.outputs:
+ if addr == txout[0]:
+ found = True
+ break
+ return found
+
+
+ def get_value(self, addresses, prevout_values):
+ # return the balance for that tx
+ is_send = False
+ is_pruned = False
+ v_in = v_out = v_out_mine = 0
+
+ for item in self.inputs:
+ addr = item.get('address')
+ if addr in addresses:
+ is_send = True
+ key = item['prevout_hash'] + ':%d'%item['prevout_n']
+ value = prevout_values.get( key )
+ if value is None:
+ is_pruned = True
+ else:
+ v_in += value
+ else:
+ is_pruned = True
+
+ for item in self.outputs:
+ addr, value = item
+ v_out += value
+ if addr in addresses:
+ v_out_mine += value
+
+ if not is_pruned:
+ # all inputs are mine:
+ fee = v_out - v_in
+ v = v_out_mine - v_in
+ else:
+ # some inputs are mine:
+ fee = None
+ if is_send:
+ v = v_out_mine - v_out
+ else:
+ # no input is mine
+ v = v_out_mine
+
+ return is_send, v, fee
+
def test_bip32():
print "address", hash_160_to_bc_address(hash_160(K0137_compressed))
-def test_p2sh():
-
- print "2 of 2"
- pubkeys = ["04e89a79651522201d756f14b1874ae49139cc984e5782afeca30ffe84e5e6b2cfadcfe9875c490c8a1a05a4debd715dd57471af8886ab5dfbb3959d97f087f77a",
- "0455cf4a3ab68a011b18cb0a86aae2b8e9cad6c6355476de05247c57a9632d127084ac7630ad89893b43c486c5a9f7ec6158fb0feb708fa9255d5c4d44bc0858f8"]
- s = multisig_script(pubkeys)
- print "address", hash_160_to_bc_address(hash_160(s.decode('hex')), 5)
-
-
- print "Gavin's tutorial: redeem p2sh: http://blockchain.info/tx-index/30888901"
- pubkey1 = "0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86"
- pubkey2 = "04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874"
- pubkey3 = "048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213"
- pubkeys = [pubkey1, pubkey2, pubkey3]
-
- tx_for_sig = raw_tx( [(None, None, '3c9018e8d5615c306d72397f8f5eef44308c98fb576a88e030c25456b4f3a7ac', 0, 'a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087', pubkeys)],
- [('1GtpSrGhRGY5kkrNz4RykoqRQoJuG2L6DS',1000000)], for_sig = 0)
-
- print "tx for sig", tx_for_sig
-
- signature1 = "304502200187af928e9d155c4b1ac9c1c9118153239aba76774f775d7c1f9c3e106ff33c0221008822b0f658edec22274d0b6ae9de10ebf2da06b1bbdaaba4e50eb078f39e3d78"
- signature2 = "30440220795f0f4f5941a77ae032ecb9e33753788d7eb5cb0c78d805575d6b00a1d9bfed02203e1f4ad9332d1416ae01e27038e945bc9db59c732728a383a6f1ed2fb99da7a4"
-
- for pubkey in pubkeys:
- import traceback, sys
-
- public_key = ecdsa.VerifyingKey.from_string(pubkey[2:].decode('hex'), curve = SECP256k1)
-
- try:
- public_key.verify_digest( signature1.decode('hex'), Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
- print True
- except ecdsa.keys.BadSignatureError:
- #traceback.print_exc(file=sys.stdout)
- print False
-
- try:
- public_key.verify_digest( signature2.decode('hex'), Hash( tx_for_sig.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
- print True
- except ecdsa.keys.BadSignatureError:
- #traceback.print_exc(file=sys.stdout)
- print False
if __name__ == '__main__':
- #test_bip32()
- test_p2sh()
+ test_bip32()
+