import hashlib, base64, ecdsa, re
-
+from util import print_error
def rev_hex(s):
return s.decode('hex')[::-1].encode('hex')
###################################### 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
-
-
-
-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]
- s += txin['tx_hash'].decode('hex')[::-1].encode('hex') # prev hash
- s += int_to_hex(txin['index'],4) # prev index
+ @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
- 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 = multisig_script(pubkeys,2)
- 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
+ @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 raw_tx(self.inputs, self.outputs, for_sig = i)
+ return self.serialize(self.inputs, self.outputs, for_sig = i)
+
def hash(self):
return Hash(self.raw.decode('hex') )[::-1].encode('hex')
for i in range(len(self.inputs)):
txin = self.inputs[i]
+ tx_for_sig = self.serialize( self.inputs, self.outputs, for_sig = i )
if txin.get('redeemScript'):
# 1 parse the redeem script
pubkey = GetPubKey(pkey.pubkey, compressed)
keypairs[ pubkey.encode('hex') ] = sec
- # list of signatures
+ # list of already existing signatures
signatures = txin.get("signatures",[])
-
- # check if we have a key corresponding to the redeem script
- for pubkey, privkey in keypairs.items():
- if pubkey in redeem_pubkeys:
- # add signature
- 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()
-
- 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)
- signatures.append( sig.encode('hex') )
-
+ 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']]
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)
+ 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 = raw_tx( self.inputs, self.outputs )
+ self.raw = self.serialize( self.inputs, self.outputs )
def deserialize(self):
def has_address(self, addr):
- print self.inputs
- print self.outputs
-
found = False
for txin in self.inputs:
if addr == txin.get('address'):
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 = Transaction.from_io(
- [{'tx_hash':'3c9018e8d5615c306d72397f8f5eef44308c98fb576a88e030c25456b4f3a7ac', 'index':0,
- 'raw_output_script':'a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087', 'redeemScript':multisig_script(pubkeys, 2)}],
- [('1GtpSrGhRGY5kkrNz4RykoqRQoJuG2L6DS',1000000)])
-
- tx_for_sig = tx.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()
+