Add bundled pybitcointools
This commit is contained in:
parent
3eae349a0a
commit
f0b53c4cbb
14 changed files with 4947 additions and 0 deletions
27
src/lib/pybitcointools/LICENSE
Normal file
27
src/lib/pybitcointools/LICENSE
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
This code is public domain. Everyone has the right to do whatever they want
|
||||||
|
with it for any purpose.
|
||||||
|
|
||||||
|
In case your jurisdiction does not consider the above disclaimer valid or
|
||||||
|
enforceable, here's an MIT license for you:
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Vitalik Buterin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
10
src/lib/pybitcointools/__init__.py
Normal file
10
src/lib/pybitcointools/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from .py2specials import *
|
||||||
|
from .py3specials import *
|
||||||
|
from .main import *
|
||||||
|
from .transaction import *
|
||||||
|
from .deterministic import *
|
||||||
|
from .bci import *
|
||||||
|
from .composite import *
|
||||||
|
from .stealth import *
|
||||||
|
from .blocks import *
|
||||||
|
from .mnemonic import *
|
528
src/lib/pybitcointools/bci.py
Normal file
528
src/lib/pybitcointools/bci.py
Normal file
|
@ -0,0 +1,528 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
import json, re
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
from urllib.request import build_opener
|
||||||
|
except:
|
||||||
|
from urllib2 import build_opener
|
||||||
|
|
||||||
|
|
||||||
|
# Makes a request to a given URL (first arg) and optional params (second arg)
|
||||||
|
def make_request(*args):
|
||||||
|
opener = build_opener()
|
||||||
|
opener.addheaders = [('User-agent',
|
||||||
|
'Mozilla/5.0'+str(random.randrange(1000000)))]
|
||||||
|
try:
|
||||||
|
return opener.open(*args).read().strip()
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
p = e.read().strip()
|
||||||
|
except:
|
||||||
|
p = e
|
||||||
|
raise Exception(p)
|
||||||
|
|
||||||
|
|
||||||
|
def is_testnet(inp):
|
||||||
|
'''Checks if inp is a testnet address or if UTXO is a known testnet TxID'''
|
||||||
|
if isinstance(inp, (list, tuple)) and len(inp) >= 1:
|
||||||
|
return any([is_testnet(x) for x in inp])
|
||||||
|
elif not isinstance(inp, basestring): # sanity check
|
||||||
|
raise TypeError("Input must be str/unicode, not type %s" % str(type(inp)))
|
||||||
|
|
||||||
|
if not inp or (inp.lower() in ("btc", "testnet")):
|
||||||
|
pass
|
||||||
|
|
||||||
|
## ADDRESSES
|
||||||
|
if inp[0] in "123mn":
|
||||||
|
if re.match("^[2mn][a-km-zA-HJ-NP-Z0-9]{26,33}$", inp):
|
||||||
|
return True
|
||||||
|
elif re.match("^[13][a-km-zA-HJ-NP-Z0-9]{26,33}$", inp):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
#sys.stderr.write("Bad address format %s")
|
||||||
|
return None
|
||||||
|
|
||||||
|
## TXID
|
||||||
|
elif re.match('^[0-9a-fA-F]{64}$', inp):
|
||||||
|
base_url = "http://api.blockcypher.com/v1/btc/{network}/txs/{txid}?includesHex=false"
|
||||||
|
try:
|
||||||
|
# try testnet fetchtx
|
||||||
|
make_request(base_url.format(network="test3", txid=inp.lower()))
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
# try mainnet fetchtx
|
||||||
|
make_request(base_url.format(network="main", txid=inp.lower()))
|
||||||
|
return False
|
||||||
|
sys.stderr.write("TxID %s has no match for testnet or mainnet (Bad TxID)")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
raise TypeError("{0} is unknown input".format(inp))
|
||||||
|
|
||||||
|
|
||||||
|
def set_network(*args):
|
||||||
|
'''Decides if args for unspent/fetchtx/pushtx are mainnet or testnet'''
|
||||||
|
r = []
|
||||||
|
for arg in args:
|
||||||
|
if not arg:
|
||||||
|
pass
|
||||||
|
if isinstance(arg, basestring):
|
||||||
|
r.append(is_testnet(arg))
|
||||||
|
elif isinstance(arg, (list, tuple)):
|
||||||
|
return set_network(*arg)
|
||||||
|
if any(r) and not all(r):
|
||||||
|
raise Exception("Mixed Testnet/Mainnet queries")
|
||||||
|
return "testnet" if any(r) else "btc"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_addr_args(*args):
|
||||||
|
# Valid input formats: unspent([addr1, addr2, addr3])
|
||||||
|
# unspent([addr1, addr2, addr3], network)
|
||||||
|
# unspent(addr1, addr2, addr3)
|
||||||
|
# unspent(addr1, addr2, addr3, network)
|
||||||
|
addr_args = args
|
||||||
|
network = "btc"
|
||||||
|
if len(args) == 0:
|
||||||
|
return [], 'btc'
|
||||||
|
if len(args) >= 1 and args[-1] in ('testnet', 'btc'):
|
||||||
|
network = args[-1]
|
||||||
|
addr_args = args[:-1]
|
||||||
|
if len(addr_args) == 1 and isinstance(addr_args, list):
|
||||||
|
network = set_network(*addr_args[0])
|
||||||
|
addr_args = addr_args[0]
|
||||||
|
if addr_args and isinstance(addr_args, tuple) and isinstance(addr_args[0], list):
|
||||||
|
addr_args = addr_args[0]
|
||||||
|
network = set_network(addr_args)
|
||||||
|
return network, addr_args
|
||||||
|
|
||||||
|
|
||||||
|
# Gets the unspent outputs of one or more addresses
|
||||||
|
def bci_unspent(*args):
|
||||||
|
network, addrs = parse_addr_args(*args)
|
||||||
|
u = []
|
||||||
|
for a in addrs:
|
||||||
|
try:
|
||||||
|
data = make_request('https://blockchain.info/unspent?active='+a)
|
||||||
|
except Exception as e:
|
||||||
|
if str(e) == 'No free outputs to spend':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise Exception(e)
|
||||||
|
try:
|
||||||
|
jsonobj = json.loads(data.decode("utf-8"))
|
||||||
|
for o in jsonobj["unspent_outputs"]:
|
||||||
|
h = o['tx_hash'].decode('hex')[::-1].encode('hex')
|
||||||
|
u.append({
|
||||||
|
"output": h+':'+str(o['tx_output_n']),
|
||||||
|
"value": o['value']
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
raise Exception("Failed to decode data: "+data)
|
||||||
|
return u
|
||||||
|
|
||||||
|
|
||||||
|
def blockr_unspent(*args):
|
||||||
|
# Valid input formats: blockr_unspent([addr1, addr2,addr3])
|
||||||
|
# blockr_unspent(addr1, addr2, addr3)
|
||||||
|
# blockr_unspent([addr1, addr2, addr3], network)
|
||||||
|
# blockr_unspent(addr1, addr2, addr3, network)
|
||||||
|
# Where network is 'btc' or 'testnet'
|
||||||
|
network, addr_args = parse_addr_args(*args)
|
||||||
|
|
||||||
|
if network == 'testnet':
|
||||||
|
blockr_url = 'http://tbtc.blockr.io/api/v1/address/unspent/'
|
||||||
|
elif network == 'btc':
|
||||||
|
blockr_url = 'http://btc.blockr.io/api/v1/address/unspent/'
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
'Unsupported network {0} for blockr_unspent'.format(network))
|
||||||
|
|
||||||
|
if len(addr_args) == 0:
|
||||||
|
return []
|
||||||
|
elif isinstance(addr_args[0], list):
|
||||||
|
addrs = addr_args[0]
|
||||||
|
else:
|
||||||
|
addrs = addr_args
|
||||||
|
res = make_request(blockr_url+','.join(addrs))
|
||||||
|
data = json.loads(res.decode("utf-8"))['data']
|
||||||
|
o = []
|
||||||
|
if 'unspent' in data:
|
||||||
|
data = [data]
|
||||||
|
for dat in data:
|
||||||
|
for u in dat['unspent']:
|
||||||
|
o.append({
|
||||||
|
"output": u['tx']+':'+str(u['n']),
|
||||||
|
"value": int(u['amount'].replace('.', ''))
|
||||||
|
})
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def helloblock_unspent(*args):
|
||||||
|
addrs, network = parse_addr_args(*args)
|
||||||
|
if network == 'testnet':
|
||||||
|
url = 'https://testnet.helloblock.io/v1/addresses/%s/unspents?limit=500&offset=%s'
|
||||||
|
elif network == 'btc':
|
||||||
|
url = 'https://mainnet.helloblock.io/v1/addresses/%s/unspents?limit=500&offset=%s'
|
||||||
|
o = []
|
||||||
|
for addr in addrs:
|
||||||
|
for offset in xrange(0, 10**9, 500):
|
||||||
|
res = make_request(url % (addr, offset))
|
||||||
|
data = json.loads(res.decode("utf-8"))["data"]
|
||||||
|
if not len(data["unspents"]):
|
||||||
|
break
|
||||||
|
elif offset:
|
||||||
|
sys.stderr.write("Getting more unspents: %d\n" % offset)
|
||||||
|
for dat in data["unspents"]:
|
||||||
|
o.append({
|
||||||
|
"output": dat["txHash"]+':'+str(dat["index"]),
|
||||||
|
"value": dat["value"],
|
||||||
|
})
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
unspent_getters = {
|
||||||
|
'bci': bci_unspent,
|
||||||
|
'blockr': blockr_unspent,
|
||||||
|
'helloblock': helloblock_unspent
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def unspent(*args, **kwargs):
|
||||||
|
f = unspent_getters.get(kwargs.get('source', ''), bci_unspent)
|
||||||
|
return f(*args)
|
||||||
|
|
||||||
|
|
||||||
|
# Gets the transaction output history of a given set of addresses,
|
||||||
|
# including whether or not they have been spent
|
||||||
|
def history(*args):
|
||||||
|
# Valid input formats: history([addr1, addr2,addr3])
|
||||||
|
# history(addr1, addr2, addr3)
|
||||||
|
if len(args) == 0:
|
||||||
|
return []
|
||||||
|
elif isinstance(args[0], list):
|
||||||
|
addrs = args[0]
|
||||||
|
else:
|
||||||
|
addrs = args
|
||||||
|
|
||||||
|
txs = []
|
||||||
|
for addr in addrs:
|
||||||
|
offset = 0
|
||||||
|
while 1:
|
||||||
|
gathered = False
|
||||||
|
while not gathered:
|
||||||
|
try:
|
||||||
|
data = make_request(
|
||||||
|
'https://blockchain.info/address/%s?format=json&offset=%s' %
|
||||||
|
(addr, offset))
|
||||||
|
gathered = True
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
sys.stderr.write(e.read().strip())
|
||||||
|
except:
|
||||||
|
sys.stderr.write(str(e))
|
||||||
|
gathered = False
|
||||||
|
try:
|
||||||
|
jsonobj = json.loads(data.decode("utf-8"))
|
||||||
|
except:
|
||||||
|
raise Exception("Failed to decode data: "+data)
|
||||||
|
txs.extend(jsonobj["txs"])
|
||||||
|
if len(jsonobj["txs"]) < 50:
|
||||||
|
break
|
||||||
|
offset += 50
|
||||||
|
sys.stderr.write("Fetching more transactions... "+str(offset)+'\n')
|
||||||
|
outs = {}
|
||||||
|
for tx in txs:
|
||||||
|
for o in tx["out"]:
|
||||||
|
if o.get('addr', None) in addrs:
|
||||||
|
key = str(tx["tx_index"])+':'+str(o["n"])
|
||||||
|
outs[key] = {
|
||||||
|
"address": o["addr"],
|
||||||
|
"value": o["value"],
|
||||||
|
"output": tx["hash"]+':'+str(o["n"]),
|
||||||
|
"block_height": tx.get("block_height", None)
|
||||||
|
}
|
||||||
|
for tx in txs:
|
||||||
|
for i, inp in enumerate(tx["inputs"]):
|
||||||
|
if "prev_out" in inp:
|
||||||
|
if inp["prev_out"].get("addr", None) in addrs:
|
||||||
|
key = str(inp["prev_out"]["tx_index"]) + \
|
||||||
|
':'+str(inp["prev_out"]["n"])
|
||||||
|
if outs.get(key):
|
||||||
|
outs[key]["spend"] = tx["hash"]+':'+str(i)
|
||||||
|
return [outs[k] for k in outs]
|
||||||
|
|
||||||
|
|
||||||
|
# Pushes a transaction to the network using https://blockchain.info/pushtx
|
||||||
|
def bci_pushtx(tx):
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = tx.encode('hex')
|
||||||
|
return make_request('https://blockchain.info/pushtx', 'tx='+tx)
|
||||||
|
|
||||||
|
|
||||||
|
def eligius_pushtx(tx):
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = tx.encode('hex')
|
||||||
|
s = make_request(
|
||||||
|
'http://eligius.st/~wizkid057/newstats/pushtxn.php',
|
||||||
|
'transaction='+tx+'&send=Push')
|
||||||
|
strings = re.findall('string[^"]*"[^"]*"', s)
|
||||||
|
for string in strings:
|
||||||
|
quote = re.findall('"[^"]*"', string)[0]
|
||||||
|
if len(quote) >= 5:
|
||||||
|
return quote[1:-1]
|
||||||
|
|
||||||
|
|
||||||
|
def blockr_pushtx(tx, network='btc'):
|
||||||
|
if network == 'testnet':
|
||||||
|
blockr_url = 'http://tbtc.blockr.io/api/v1/tx/push'
|
||||||
|
elif network == 'btc':
|
||||||
|
blockr_url = 'http://btc.blockr.io/api/v1/tx/push'
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
'Unsupported network {0} for blockr_pushtx'.format(network))
|
||||||
|
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = tx.encode('hex')
|
||||||
|
return make_request(blockr_url, '{"hex":"%s"}' % tx)
|
||||||
|
|
||||||
|
|
||||||
|
def helloblock_pushtx(tx):
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = tx.encode('hex')
|
||||||
|
return make_request('https://mainnet.helloblock.io/v1/transactions',
|
||||||
|
'rawTxHex='+tx)
|
||||||
|
|
||||||
|
pushtx_getters = {
|
||||||
|
'bci': bci_pushtx,
|
||||||
|
'blockr': blockr_pushtx,
|
||||||
|
'helloblock': helloblock_pushtx
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def pushtx(*args, **kwargs):
|
||||||
|
f = pushtx_getters.get(kwargs.get('source', ''), bci_pushtx)
|
||||||
|
return f(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def last_block_height(network='btc'):
|
||||||
|
if network == 'testnet':
|
||||||
|
data = make_request('http://tbtc.blockr.io/api/v1/block/info/last')
|
||||||
|
jsonobj = json.loads(data.decode("utf-8"))
|
||||||
|
return jsonobj["data"]["nb"]
|
||||||
|
|
||||||
|
data = make_request('https://blockchain.info/latestblock')
|
||||||
|
jsonobj = json.loads(data.decode("utf-8"))
|
||||||
|
return jsonobj["height"]
|
||||||
|
|
||||||
|
|
||||||
|
# Gets a specific transaction
|
||||||
|
def bci_fetchtx(txhash):
|
||||||
|
if isinstance(txhash, list):
|
||||||
|
return [bci_fetchtx(h) for h in txhash]
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', txhash):
|
||||||
|
txhash = txhash.encode('hex')
|
||||||
|
data = make_request('https://blockchain.info/rawtx/'+txhash+'?format=hex')
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def blockr_fetchtx(txhash, network='btc'):
|
||||||
|
if network == 'testnet':
|
||||||
|
blockr_url = 'http://tbtc.blockr.io/api/v1/tx/raw/'
|
||||||
|
elif network == 'btc':
|
||||||
|
blockr_url = 'http://btc.blockr.io/api/v1/tx/raw/'
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
'Unsupported network {0} for blockr_fetchtx'.format(network))
|
||||||
|
if isinstance(txhash, list):
|
||||||
|
txhash = ','.join([x.encode('hex') if not re.match('^[0-9a-fA-F]*$', x)
|
||||||
|
else x for x in txhash])
|
||||||
|
jsondata = json.loads(make_request(blockr_url+txhash).decode("utf-8"))
|
||||||
|
return [d['tx']['hex'] for d in jsondata['data']]
|
||||||
|
else:
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', txhash):
|
||||||
|
txhash = txhash.encode('hex')
|
||||||
|
jsondata = json.loads(make_request(blockr_url+txhash).decode("utf-8"))
|
||||||
|
return jsondata['data']['tx']['hex']
|
||||||
|
|
||||||
|
|
||||||
|
def helloblock_fetchtx(txhash, network='btc'):
|
||||||
|
if isinstance(txhash, list):
|
||||||
|
return [helloblock_fetchtx(h) for h in txhash]
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', txhash):
|
||||||
|
txhash = txhash.encode('hex')
|
||||||
|
if network == 'testnet':
|
||||||
|
url = 'https://testnet.helloblock.io/v1/transactions/'
|
||||||
|
elif network == 'btc':
|
||||||
|
url = 'https://mainnet.helloblock.io/v1/transactions/'
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
'Unsupported network {0} for helloblock_fetchtx'.format(network))
|
||||||
|
data = json.loads(make_request(url + txhash).decode("utf-8"))["data"]["transaction"]
|
||||||
|
o = {
|
||||||
|
"locktime": data["locktime"],
|
||||||
|
"version": data["version"],
|
||||||
|
"ins": [],
|
||||||
|
"outs": []
|
||||||
|
}
|
||||||
|
for inp in data["inputs"]:
|
||||||
|
o["ins"].append({
|
||||||
|
"script": inp["scriptSig"],
|
||||||
|
"outpoint": {
|
||||||
|
"index": inp["prevTxoutIndex"],
|
||||||
|
"hash": inp["prevTxHash"],
|
||||||
|
},
|
||||||
|
"sequence": 4294967295
|
||||||
|
})
|
||||||
|
for outp in data["outputs"]:
|
||||||
|
o["outs"].append({
|
||||||
|
"value": outp["value"],
|
||||||
|
"script": outp["scriptPubKey"]
|
||||||
|
})
|
||||||
|
from .transaction import serialize
|
||||||
|
from .transaction import txhash as TXHASH
|
||||||
|
tx = serialize(o)
|
||||||
|
assert TXHASH(tx) == txhash
|
||||||
|
return tx
|
||||||
|
|
||||||
|
|
||||||
|
fetchtx_getters = {
|
||||||
|
'bci': bci_fetchtx,
|
||||||
|
'blockr': blockr_fetchtx,
|
||||||
|
'helloblock': helloblock_fetchtx
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fetchtx(*args, **kwargs):
|
||||||
|
f = fetchtx_getters.get(kwargs.get('source', ''), bci_fetchtx)
|
||||||
|
return f(*args)
|
||||||
|
|
||||||
|
|
||||||
|
def firstbits(address):
|
||||||
|
if len(address) >= 25:
|
||||||
|
return make_request('https://blockchain.info/q/getfirstbits/'+address)
|
||||||
|
else:
|
||||||
|
return make_request(
|
||||||
|
'https://blockchain.info/q/resolvefirstbits/'+address)
|
||||||
|
|
||||||
|
|
||||||
|
def get_block_at_height(height):
|
||||||
|
j = json.loads(make_request("https://blockchain.info/block-height/" +
|
||||||
|
str(height)+"?format=json").decode("utf-8"))
|
||||||
|
for b in j['blocks']:
|
||||||
|
if b['main_chain'] is True:
|
||||||
|
return b
|
||||||
|
raise Exception("Block at this height not found")
|
||||||
|
|
||||||
|
|
||||||
|
def _get_block(inp):
|
||||||
|
if len(str(inp)) < 64:
|
||||||
|
return get_block_at_height(inp)
|
||||||
|
else:
|
||||||
|
return json.loads(make_request(
|
||||||
|
'https://blockchain.info/rawblock/'+inp).decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def bci_get_block_header_data(inp):
|
||||||
|
j = _get_block(inp)
|
||||||
|
return {
|
||||||
|
'version': j['ver'],
|
||||||
|
'hash': j['hash'],
|
||||||
|
'prevhash': j['prev_block'],
|
||||||
|
'timestamp': j['time'],
|
||||||
|
'merkle_root': j['mrkl_root'],
|
||||||
|
'bits': j['bits'],
|
||||||
|
'nonce': j['nonce'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def blockr_get_block_header_data(height, network='btc'):
|
||||||
|
if network == 'testnet':
|
||||||
|
blockr_url = "http://tbtc.blockr.io/api/v1/block/raw/"
|
||||||
|
elif network == 'btc':
|
||||||
|
blockr_url = "http://btc.blockr.io/api/v1/block/raw/"
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
'Unsupported network {0} for blockr_get_block_header_data'.format(network))
|
||||||
|
|
||||||
|
k = json.loads(make_request(blockr_url + str(height)).decode("utf-8"))
|
||||||
|
j = k['data']
|
||||||
|
return {
|
||||||
|
'version': j['version'],
|
||||||
|
'hash': j['hash'],
|
||||||
|
'prevhash': j['previousblockhash'],
|
||||||
|
'timestamp': j['time'],
|
||||||
|
'merkle_root': j['merkleroot'],
|
||||||
|
'bits': int(j['bits'], 16),
|
||||||
|
'nonce': j['nonce'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_block_timestamp(height, network='btc'):
|
||||||
|
if network == 'testnet':
|
||||||
|
blockr_url = "http://tbtc.blockr.io/api/v1/block/info/"
|
||||||
|
elif network == 'btc':
|
||||||
|
blockr_url = "http://btc.blockr.io/api/v1/block/info/"
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
'Unsupported network {0} for get_block_timestamp'.format(network))
|
||||||
|
|
||||||
|
import time, calendar
|
||||||
|
if isinstance(height, list):
|
||||||
|
k = json.loads(make_request(blockr_url + ','.join([str(x) for x in height])).decode("utf-8"))
|
||||||
|
o = {x['nb']: calendar.timegm(time.strptime(x['time_utc'],
|
||||||
|
"%Y-%m-%dT%H:%M:%SZ")) for x in k['data']}
|
||||||
|
return [o[x] for x in height]
|
||||||
|
else:
|
||||||
|
k = json.loads(make_request(blockr_url + str(height)).decode("utf-8"))
|
||||||
|
j = k['data']['time_utc']
|
||||||
|
return calendar.timegm(time.strptime(j, "%Y-%m-%dT%H:%M:%SZ"))
|
||||||
|
|
||||||
|
|
||||||
|
block_header_data_getters = {
|
||||||
|
'bci': bci_get_block_header_data,
|
||||||
|
'blockr': blockr_get_block_header_data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_block_header_data(inp, **kwargs):
|
||||||
|
f = block_header_data_getters.get(kwargs.get('source', ''),
|
||||||
|
bci_get_block_header_data)
|
||||||
|
return f(inp, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def get_txs_in_block(inp):
|
||||||
|
j = _get_block(inp)
|
||||||
|
hashes = [t['hash'] for t in j['tx']]
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
|
||||||
|
def get_block_height(txhash):
|
||||||
|
j = json.loads(make_request('https://blockchain.info/rawtx/'+txhash).decode("utf-8"))
|
||||||
|
return j['block_height']
|
||||||
|
|
||||||
|
# fromAddr, toAddr, 12345, changeAddress
|
||||||
|
def get_tx_composite(inputs, outputs, output_value, change_address=None, network=None):
|
||||||
|
"""mktx using blockcypher API"""
|
||||||
|
inputs = [inputs] if not isinstance(inputs, list) else inputs
|
||||||
|
outputs = [outputs] if not isinstance(outputs, list) else outputs
|
||||||
|
network = set_network(change_address or inputs) if not network else network.lower()
|
||||||
|
url = "http://api.blockcypher.com/v1/btc/{network}/txs/new?includeToSignTx=true".format(
|
||||||
|
network=('test3' if network=='testnet' else 'main'))
|
||||||
|
is_address = lambda a: bool(re.match("^[123mn][a-km-zA-HJ-NP-Z0-9]{26,33}$", a))
|
||||||
|
if any([is_address(x) for x in inputs]):
|
||||||
|
inputs_type = 'addresses' # also accepts UTXOs, only addresses supported presently
|
||||||
|
if any([is_address(x) for x in outputs]):
|
||||||
|
outputs_type = 'addresses' # TODO: add UTXO support
|
||||||
|
data = {
|
||||||
|
'inputs': [{inputs_type: inputs}],
|
||||||
|
'confirmations': 0,
|
||||||
|
'preference': 'high',
|
||||||
|
'outputs': [{outputs_type: outputs, "value": output_value}]
|
||||||
|
}
|
||||||
|
if change_address:
|
||||||
|
data["change_address"] = change_address #
|
||||||
|
jdata = json.loads(make_request(url, data))
|
||||||
|
hash, txh = jdata.get("tosign")[0], jdata.get("tosign_tx")[0]
|
||||||
|
assert bin_dbl_sha256(txh.decode('hex')).encode('hex') == hash, "checksum mismatch %s" % hash
|
||||||
|
return txh.encode("utf-8")
|
||||||
|
|
||||||
|
blockcypher_mktx = get_tx_composite
|
50
src/lib/pybitcointools/blocks.py
Normal file
50
src/lib/pybitcointools/blocks.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from .main import *
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_header(inp):
|
||||||
|
o = encode(inp['version'], 256, 4)[::-1] + \
|
||||||
|
inp['prevhash'].decode('hex')[::-1] + \
|
||||||
|
inp['merkle_root'].decode('hex')[::-1] + \
|
||||||
|
encode(inp['timestamp'], 256, 4)[::-1] + \
|
||||||
|
encode(inp['bits'], 256, 4)[::-1] + \
|
||||||
|
encode(inp['nonce'], 256, 4)[::-1]
|
||||||
|
h = bin_sha256(bin_sha256(o))[::-1].encode('hex')
|
||||||
|
assert h == inp['hash'], (sha256(o), inp['hash'])
|
||||||
|
return o.encode('hex')
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_header(inp):
|
||||||
|
inp = inp.decode('hex')
|
||||||
|
return {
|
||||||
|
"version": decode(inp[:4][::-1], 256),
|
||||||
|
"prevhash": inp[4:36][::-1].encode('hex'),
|
||||||
|
"merkle_root": inp[36:68][::-1].encode('hex'),
|
||||||
|
"timestamp": decode(inp[68:72][::-1], 256),
|
||||||
|
"bits": decode(inp[72:76][::-1], 256),
|
||||||
|
"nonce": decode(inp[76:80][::-1], 256),
|
||||||
|
"hash": bin_sha256(bin_sha256(inp))[::-1].encode('hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def mk_merkle_proof(header, hashes, index):
|
||||||
|
nodes = [h.decode('hex')[::-1] for h in hashes]
|
||||||
|
if len(nodes) % 2 and len(nodes) > 2:
|
||||||
|
nodes.append(nodes[-1])
|
||||||
|
layers = [nodes]
|
||||||
|
while len(nodes) > 1:
|
||||||
|
newnodes = []
|
||||||
|
for i in range(0, len(nodes) - 1, 2):
|
||||||
|
newnodes.append(bin_sha256(bin_sha256(nodes[i] + nodes[i+1])))
|
||||||
|
if len(newnodes) % 2 and len(newnodes) > 2:
|
||||||
|
newnodes.append(newnodes[-1])
|
||||||
|
nodes = newnodes
|
||||||
|
layers.append(nodes)
|
||||||
|
# Sanity check, make sure merkle root is valid
|
||||||
|
assert nodes[0][::-1].encode('hex') == header['merkle_root']
|
||||||
|
merkle_siblings = \
|
||||||
|
[layers[i][(index >> i) ^ 1] for i in range(len(layers)-1)]
|
||||||
|
return {
|
||||||
|
"hash": hashes[index],
|
||||||
|
"siblings": [x[::-1].encode('hex') for x in merkle_siblings],
|
||||||
|
"header": header
|
||||||
|
}
|
128
src/lib/pybitcointools/composite.py
Normal file
128
src/lib/pybitcointools/composite.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
from .main import *
|
||||||
|
from .transaction import *
|
||||||
|
from .bci import *
|
||||||
|
from .deterministic import *
|
||||||
|
from .blocks import *
|
||||||
|
|
||||||
|
|
||||||
|
# Takes privkey, address, value (satoshis), fee (satoshis)
|
||||||
|
def send(frm, to, value, fee=10000, **kwargs):
|
||||||
|
return sendmultitx(frm, to + ":" + str(value), fee, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# Takes privkey, "address1:value1,address2:value2" (satoshis), fee (satoshis)
|
||||||
|
def sendmultitx(frm, *args, **kwargs):
|
||||||
|
tv, fee = args[:-1], int(args[-1])
|
||||||
|
outs = []
|
||||||
|
outvalue = 0
|
||||||
|
for a in tv:
|
||||||
|
outs.append(a)
|
||||||
|
outvalue += int(a.split(":")[1])
|
||||||
|
|
||||||
|
u = unspent(privtoaddr(frm), **kwargs)
|
||||||
|
u2 = select(u, int(outvalue)+int(fee))
|
||||||
|
argz = u2 + outs + [privtoaddr(frm), fee]
|
||||||
|
tx = mksend(*argz)
|
||||||
|
tx2 = signall(tx, frm)
|
||||||
|
return pushtx(tx2, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# Takes address, address, value (satoshis), fee(satoshis)
|
||||||
|
def preparetx(frm, to, value, fee=10000, **kwargs):
|
||||||
|
tovalues = to + ":" + str(value)
|
||||||
|
return preparemultitx(frm, tovalues, fee, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# Takes address, address:value, address:value ... (satoshis), fee(satoshis)
|
||||||
|
def preparemultitx(frm, *args, **kwargs):
|
||||||
|
tv, fee = args[:-1], int(args[-1])
|
||||||
|
outs = []
|
||||||
|
outvalue = 0
|
||||||
|
for a in tv:
|
||||||
|
outs.append(a)
|
||||||
|
outvalue += int(a.split(":")[1])
|
||||||
|
|
||||||
|
u = unspent(frm, **kwargs)
|
||||||
|
u2 = select(u, int(outvalue)+int(fee))
|
||||||
|
argz = u2 + outs + [frm, fee]
|
||||||
|
return mksend(*argz)
|
||||||
|
|
||||||
|
|
||||||
|
# BIP32 hierarchical deterministic multisig script
|
||||||
|
def bip32_hdm_script(*args):
|
||||||
|
if len(args) == 3:
|
||||||
|
keys, req, path = args
|
||||||
|
else:
|
||||||
|
i, keys, path = 0, [], []
|
||||||
|
while len(args[i]) > 40:
|
||||||
|
keys.append(args[i])
|
||||||
|
i += 1
|
||||||
|
req = int(args[i])
|
||||||
|
path = map(int, args[i+1:])
|
||||||
|
pubs = sorted(map(lambda x: bip32_descend(x, path), keys))
|
||||||
|
return mk_multisig_script(pubs, req)
|
||||||
|
|
||||||
|
|
||||||
|
# BIP32 hierarchical deterministic multisig address
|
||||||
|
def bip32_hdm_addr(*args):
|
||||||
|
return scriptaddr(bip32_hdm_script(*args))
|
||||||
|
|
||||||
|
|
||||||
|
# Setup a coinvault transaction
|
||||||
|
def setup_coinvault_tx(tx, script):
|
||||||
|
txobj = deserialize(tx)
|
||||||
|
N = deserialize_script(script)[-2]
|
||||||
|
for inp in txobj["ins"]:
|
||||||
|
inp["script"] = serialize_script([None] * (N+1) + [script])
|
||||||
|
return serialize(txobj)
|
||||||
|
|
||||||
|
|
||||||
|
# Sign a coinvault transaction
|
||||||
|
def sign_coinvault_tx(tx, priv):
|
||||||
|
pub = privtopub(priv)
|
||||||
|
txobj = deserialize(tx)
|
||||||
|
subscript = deserialize_script(txobj['ins'][0]['script'])
|
||||||
|
oscript = deserialize_script(subscript[-1])
|
||||||
|
k, pubs = oscript[0], oscript[1:-2]
|
||||||
|
for j in range(len(txobj['ins'])):
|
||||||
|
scr = deserialize_script(txobj['ins'][j]['script'])
|
||||||
|
for i, p in enumerate(pubs):
|
||||||
|
if p == pub:
|
||||||
|
scr[i+1] = multisign(tx, j, subscript[-1], priv)
|
||||||
|
if len(filter(lambda x: x, scr[1:-1])) >= k:
|
||||||
|
scr = [None] + filter(lambda x: x, scr[1:-1])[:k] + [scr[-1]]
|
||||||
|
txobj['ins'][j]['script'] = serialize_script(scr)
|
||||||
|
return serialize(txobj)
|
||||||
|
|
||||||
|
|
||||||
|
# Inspects a transaction
|
||||||
|
def inspect(tx, **kwargs):
|
||||||
|
d = deserialize(tx)
|
||||||
|
isum = 0
|
||||||
|
ins = {}
|
||||||
|
for _in in d['ins']:
|
||||||
|
h = _in['outpoint']['hash']
|
||||||
|
i = _in['outpoint']['index']
|
||||||
|
prevout = deserialize(fetchtx(h, **kwargs))['outs'][i]
|
||||||
|
isum += prevout['value']
|
||||||
|
a = script_to_address(prevout['script'])
|
||||||
|
ins[a] = ins.get(a, 0) + prevout['value']
|
||||||
|
outs = []
|
||||||
|
osum = 0
|
||||||
|
for _out in d['outs']:
|
||||||
|
outs.append({'address': script_to_address(_out['script']),
|
||||||
|
'value': _out['value']})
|
||||||
|
osum += _out['value']
|
||||||
|
return {
|
||||||
|
'fee': isum - osum,
|
||||||
|
'outs': outs,
|
||||||
|
'ins': ins
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def merkle_prove(txhash):
|
||||||
|
blocknum = str(get_block_height(txhash))
|
||||||
|
header = get_block_header_data(blocknum)
|
||||||
|
hashes = get_txs_in_block(blocknum)
|
||||||
|
i = hashes.index(txhash)
|
||||||
|
return mk_merkle_proof(header, hashes, i)
|
199
src/lib/pybitcointools/deterministic.py
Normal file
199
src/lib/pybitcointools/deterministic.py
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
from .main import *
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
from binascii import hexlify
|
||||||
|
# Electrum wallets
|
||||||
|
|
||||||
|
|
||||||
|
def electrum_stretch(seed):
|
||||||
|
return slowsha(seed)
|
||||||
|
|
||||||
|
# Accepts seed or stretched seed, returns master public key
|
||||||
|
|
||||||
|
|
||||||
|
def electrum_mpk(seed):
|
||||||
|
if len(seed) == 32:
|
||||||
|
seed = electrum_stretch(seed)
|
||||||
|
return privkey_to_pubkey(seed)[2:]
|
||||||
|
|
||||||
|
# Accepts (seed or stretched seed), index and secondary index
|
||||||
|
# (conventionally 0 for ordinary addresses, 1 for change) , returns privkey
|
||||||
|
|
||||||
|
|
||||||
|
def electrum_privkey(seed, n, for_change=0):
|
||||||
|
if len(seed) == 32:
|
||||||
|
seed = electrum_stretch(seed)
|
||||||
|
mpk = electrum_mpk(seed)
|
||||||
|
offset = dbl_sha256(from_int_representation_to_bytes(n)+b':'+from_int_representation_to_bytes(for_change)+b':'+binascii.unhexlify(mpk))
|
||||||
|
return add_privkeys(seed, offset)
|
||||||
|
|
||||||
|
# Accepts (seed or stretched seed or master pubkey), index and secondary index
|
||||||
|
# (conventionally 0 for ordinary addresses, 1 for change) , returns pubkey
|
||||||
|
|
||||||
|
|
||||||
|
def electrum_pubkey(masterkey, n, for_change=0):
|
||||||
|
if len(masterkey) == 32:
|
||||||
|
mpk = electrum_mpk(electrum_stretch(masterkey))
|
||||||
|
elif len(masterkey) == 64:
|
||||||
|
mpk = electrum_mpk(masterkey)
|
||||||
|
else:
|
||||||
|
mpk = masterkey
|
||||||
|
bin_mpk = encode_pubkey(mpk, 'bin_electrum')
|
||||||
|
offset = bin_dbl_sha256(from_int_representation_to_bytes(n)+b':'+from_int_representation_to_bytes(for_change)+b':'+bin_mpk)
|
||||||
|
return add_pubkeys('04'+mpk, privtopub(offset))
|
||||||
|
|
||||||
|
# seed/stretched seed/pubkey -> address (convenience method)
|
||||||
|
|
||||||
|
|
||||||
|
def electrum_address(masterkey, n, for_change=0, version=0):
|
||||||
|
return pubkey_to_address(electrum_pubkey(masterkey, n, for_change), version)
|
||||||
|
|
||||||
|
# Given a master public key, a private key from that wallet and its index,
|
||||||
|
# cracks the secret exponent which can be used to generate all other private
|
||||||
|
# keys in the wallet
|
||||||
|
|
||||||
|
|
||||||
|
def crack_electrum_wallet(mpk, pk, n, for_change=0):
|
||||||
|
bin_mpk = encode_pubkey(mpk, 'bin_electrum')
|
||||||
|
offset = dbl_sha256(str(n)+':'+str(for_change)+':'+bin_mpk)
|
||||||
|
return subtract_privkeys(pk, offset)
|
||||||
|
|
||||||
|
# Below code ASSUMES binary inputs and compressed pubkeys
|
||||||
|
MAINNET_PRIVATE = b'\x04\x88\xAD\xE4'
|
||||||
|
MAINNET_PUBLIC = b'\x04\x88\xB2\x1E'
|
||||||
|
TESTNET_PRIVATE = b'\x04\x35\x83\x94'
|
||||||
|
TESTNET_PUBLIC = b'\x04\x35\x87\xCF'
|
||||||
|
PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE]
|
||||||
|
PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC]
|
||||||
|
|
||||||
|
# BIP32 child key derivation
|
||||||
|
|
||||||
|
|
||||||
|
def raw_bip32_ckd(rawtuple, i):
|
||||||
|
vbytes, depth, fingerprint, oldi, chaincode, key = rawtuple
|
||||||
|
i = int(i)
|
||||||
|
|
||||||
|
if vbytes in PRIVATE:
|
||||||
|
priv = key
|
||||||
|
pub = privtopub(key)
|
||||||
|
else:
|
||||||
|
pub = key
|
||||||
|
|
||||||
|
if i >= 2**31:
|
||||||
|
if vbytes in PUBLIC:
|
||||||
|
raise Exception("Can't do private derivation on public key!")
|
||||||
|
I = hmac.new(chaincode, b'\x00'+priv[:32]+encode(i, 256, 4), hashlib.sha512).digest()
|
||||||
|
else:
|
||||||
|
I = hmac.new(chaincode, pub+encode(i, 256, 4), hashlib.sha512).digest()
|
||||||
|
|
||||||
|
if vbytes in PRIVATE:
|
||||||
|
newkey = add_privkeys(I[:32]+B'\x01', priv)
|
||||||
|
fingerprint = bin_hash160(privtopub(key))[:4]
|
||||||
|
if vbytes in PUBLIC:
|
||||||
|
newkey = add_pubkeys(compress(privtopub(I[:32])), key)
|
||||||
|
fingerprint = bin_hash160(key)[:4]
|
||||||
|
|
||||||
|
return (vbytes, depth + 1, fingerprint, i, I[32:], newkey)
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_serialize(rawtuple):
|
||||||
|
vbytes, depth, fingerprint, i, chaincode, key = rawtuple
|
||||||
|
i = encode(i, 256, 4)
|
||||||
|
chaincode = encode(hash_to_int(chaincode), 256, 32)
|
||||||
|
keydata = b'\x00'+key[:-1] if vbytes in PRIVATE else key
|
||||||
|
bindata = vbytes + from_int_to_byte(depth % 256) + fingerprint + i + chaincode + keydata
|
||||||
|
return changebase(bindata+bin_dbl_sha256(bindata)[:4], 256, 58)
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_deserialize(data):
|
||||||
|
dbin = changebase(data, 58, 256)
|
||||||
|
if bin_dbl_sha256(dbin[:-4])[:4] != dbin[-4:]:
|
||||||
|
raise Exception("Invalid checksum")
|
||||||
|
vbytes = dbin[0:4]
|
||||||
|
depth = from_byte_to_int(dbin[4])
|
||||||
|
fingerprint = dbin[5:9]
|
||||||
|
i = decode(dbin[9:13], 256)
|
||||||
|
chaincode = dbin[13:45]
|
||||||
|
key = dbin[46:78]+b'\x01' if vbytes in PRIVATE else dbin[45:78]
|
||||||
|
return (vbytes, depth, fingerprint, i, chaincode, key)
|
||||||
|
|
||||||
|
|
||||||
|
def raw_bip32_privtopub(rawtuple):
|
||||||
|
vbytes, depth, fingerprint, i, chaincode, key = rawtuple
|
||||||
|
newvbytes = MAINNET_PUBLIC if vbytes == MAINNET_PRIVATE else TESTNET_PUBLIC
|
||||||
|
return (newvbytes, depth, fingerprint, i, chaincode, privtopub(key))
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_privtopub(data):
|
||||||
|
return bip32_serialize(raw_bip32_privtopub(bip32_deserialize(data)))
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_ckd(data, i):
|
||||||
|
return bip32_serialize(raw_bip32_ckd(bip32_deserialize(data), i))
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_master_key(seed, vbytes=MAINNET_PRIVATE):
|
||||||
|
I = hmac.new(from_string_to_bytes("Bitcoin seed"), seed, hashlib.sha512).digest()
|
||||||
|
return bip32_serialize((vbytes, 0, b'\x00'*4, 0, I[32:], I[:32]+b'\x01'))
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_bin_extract_key(data):
|
||||||
|
return bip32_deserialize(data)[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_extract_key(data):
|
||||||
|
return safe_hexlify(bip32_deserialize(data)[-1])
|
||||||
|
|
||||||
|
# Exploits the same vulnerability as above in Electrum wallets
|
||||||
|
# Takes a BIP32 pubkey and one of the child privkeys of its corresponding
|
||||||
|
# privkey and returns the BIP32 privkey associated with that pubkey
|
||||||
|
|
||||||
|
|
||||||
|
def raw_crack_bip32_privkey(parent_pub, priv):
|
||||||
|
vbytes, depth, fingerprint, i, chaincode, key = priv
|
||||||
|
pvbytes, pdepth, pfingerprint, pi, pchaincode, pkey = parent_pub
|
||||||
|
i = int(i)
|
||||||
|
|
||||||
|
if i >= 2**31:
|
||||||
|
raise Exception("Can't crack private derivation!")
|
||||||
|
|
||||||
|
I = hmac.new(pchaincode, pkey+encode(i, 256, 4), hashlib.sha512).digest()
|
||||||
|
|
||||||
|
pprivkey = subtract_privkeys(key, I[:32]+b'\x01')
|
||||||
|
|
||||||
|
newvbytes = MAINNET_PRIVATE if vbytes == MAINNET_PUBLIC else TESTNET_PRIVATE
|
||||||
|
return (newvbytes, pdepth, pfingerprint, pi, pchaincode, pprivkey)
|
||||||
|
|
||||||
|
|
||||||
|
def crack_bip32_privkey(parent_pub, priv):
|
||||||
|
dsppub = bip32_deserialize(parent_pub)
|
||||||
|
dspriv = bip32_deserialize(priv)
|
||||||
|
return bip32_serialize(raw_crack_bip32_privkey(dsppub, dspriv))
|
||||||
|
|
||||||
|
|
||||||
|
def coinvault_pub_to_bip32(*args):
|
||||||
|
if len(args) == 1:
|
||||||
|
args = args[0].split(' ')
|
||||||
|
vals = map(int, args[34:])
|
||||||
|
I1 = ''.join(map(chr, vals[:33]))
|
||||||
|
I2 = ''.join(map(chr, vals[35:67]))
|
||||||
|
return bip32_serialize((MAINNET_PUBLIC, 0, b'\x00'*4, 0, I2, I1))
|
||||||
|
|
||||||
|
|
||||||
|
def coinvault_priv_to_bip32(*args):
|
||||||
|
if len(args) == 1:
|
||||||
|
args = args[0].split(' ')
|
||||||
|
vals = map(int, args[34:])
|
||||||
|
I2 = ''.join(map(chr, vals[35:67]))
|
||||||
|
I3 = ''.join(map(chr, vals[72:104]))
|
||||||
|
return bip32_serialize((MAINNET_PRIVATE, 0, b'\x00'*4, 0, I2, I3+b'\x01'))
|
||||||
|
|
||||||
|
|
||||||
|
def bip32_descend(*args):
|
||||||
|
if len(args) == 2 and isinstance(args[1], list):
|
||||||
|
key, path = args
|
||||||
|
else:
|
||||||
|
key, path = args[0], map(int, args[1:])
|
||||||
|
for p in path:
|
||||||
|
key = bip32_ckd(key, p)
|
||||||
|
return bip32_extract_key(key)
|
2048
src/lib/pybitcointools/english.txt
Normal file
2048
src/lib/pybitcointools/english.txt
Normal file
File diff suppressed because it is too large
Load diff
581
src/lib/pybitcointools/main.py
Normal file
581
src/lib/pybitcointools/main.py
Normal file
|
@ -0,0 +1,581 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
from .py2specials import *
|
||||||
|
from .py3specials import *
|
||||||
|
import binascii
|
||||||
|
import hashlib
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import hmac
|
||||||
|
from .ripemd import *
|
||||||
|
|
||||||
|
# Elliptic curve parameters (secp256k1)
|
||||||
|
|
||||||
|
P = 2**256 - 2**32 - 977
|
||||||
|
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
|
||||||
|
A = 0
|
||||||
|
B = 7
|
||||||
|
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
|
||||||
|
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
|
||||||
|
G = (Gx, Gy)
|
||||||
|
|
||||||
|
|
||||||
|
def change_curve(p, n, a, b, gx, gy):
|
||||||
|
global P, N, A, B, Gx, Gy, G
|
||||||
|
P, N, A, B, Gx, Gy = p, n, a, b, gx, gy
|
||||||
|
G = (Gx, Gy)
|
||||||
|
|
||||||
|
|
||||||
|
def getG():
|
||||||
|
return G
|
||||||
|
|
||||||
|
# Extended Euclidean Algorithm
|
||||||
|
|
||||||
|
|
||||||
|
def inv(a, n):
|
||||||
|
if a == 0:
|
||||||
|
return 0
|
||||||
|
lm, hm = 1, 0
|
||||||
|
low, high = a % n, n
|
||||||
|
while low > 1:
|
||||||
|
r = high//low
|
||||||
|
nm, new = hm-lm*r, high-low*r
|
||||||
|
lm, low, hm, high = nm, new, lm, low
|
||||||
|
return lm % n
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# JSON access (for pybtctool convenience)
|
||||||
|
|
||||||
|
|
||||||
|
def access(obj, prop):
|
||||||
|
if isinstance(obj, dict):
|
||||||
|
if prop in obj:
|
||||||
|
return obj[prop]
|
||||||
|
elif '.' in prop:
|
||||||
|
return obj[float(prop)]
|
||||||
|
else:
|
||||||
|
return obj[int(prop)]
|
||||||
|
else:
|
||||||
|
return obj[int(prop)]
|
||||||
|
|
||||||
|
|
||||||
|
def multiaccess(obj, prop):
|
||||||
|
return [access(o, prop) for o in obj]
|
||||||
|
|
||||||
|
|
||||||
|
def slice(obj, start=0, end=2**200):
|
||||||
|
return obj[int(start):int(end)]
|
||||||
|
|
||||||
|
|
||||||
|
def count(obj):
|
||||||
|
return len(obj)
|
||||||
|
|
||||||
|
_sum = sum
|
||||||
|
|
||||||
|
|
||||||
|
def sum(obj):
|
||||||
|
return _sum(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def isinf(p):
|
||||||
|
return p[0] == 0 and p[1] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def to_jacobian(p):
|
||||||
|
o = (p[0], p[1], 1)
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def jacobian_double(p):
|
||||||
|
if not p[1]:
|
||||||
|
return (0, 0, 0)
|
||||||
|
ysq = (p[1] ** 2) % P
|
||||||
|
S = (4 * p[0] * ysq) % P
|
||||||
|
M = (3 * p[0] ** 2 + A * p[2] ** 4) % P
|
||||||
|
nx = (M**2 - 2 * S) % P
|
||||||
|
ny = (M * (S - nx) - 8 * ysq ** 2) % P
|
||||||
|
nz = (2 * p[1] * p[2]) % P
|
||||||
|
return (nx, ny, nz)
|
||||||
|
|
||||||
|
|
||||||
|
def jacobian_add(p, q):
|
||||||
|
if not p[1]:
|
||||||
|
return q
|
||||||
|
if not q[1]:
|
||||||
|
return p
|
||||||
|
U1 = (p[0] * q[2] ** 2) % P
|
||||||
|
U2 = (q[0] * p[2] ** 2) % P
|
||||||
|
S1 = (p[1] * q[2] ** 3) % P
|
||||||
|
S2 = (q[1] * p[2] ** 3) % P
|
||||||
|
if U1 == U2:
|
||||||
|
if S1 != S2:
|
||||||
|
return (0, 0, 1)
|
||||||
|
return jacobian_double(p)
|
||||||
|
H = U2 - U1
|
||||||
|
R = S2 - S1
|
||||||
|
H2 = (H * H) % P
|
||||||
|
H3 = (H * H2) % P
|
||||||
|
U1H2 = (U1 * H2) % P
|
||||||
|
nx = (R ** 2 - H3 - 2 * U1H2) % P
|
||||||
|
ny = (R * (U1H2 - nx) - S1 * H3) % P
|
||||||
|
nz = (H * p[2] * q[2]) % P
|
||||||
|
return (nx, ny, nz)
|
||||||
|
|
||||||
|
|
||||||
|
def from_jacobian(p):
|
||||||
|
z = inv(p[2], P)
|
||||||
|
return ((p[0] * z**2) % P, (p[1] * z**3) % P)
|
||||||
|
|
||||||
|
|
||||||
|
def jacobian_multiply(a, n):
|
||||||
|
if a[1] == 0 or n == 0:
|
||||||
|
return (0, 0, 1)
|
||||||
|
if n == 1:
|
||||||
|
return a
|
||||||
|
if n < 0 or n >= N:
|
||||||
|
return jacobian_multiply(a, n % N)
|
||||||
|
if (n % 2) == 0:
|
||||||
|
return jacobian_double(jacobian_multiply(a, n//2))
|
||||||
|
if (n % 2) == 1:
|
||||||
|
return jacobian_add(jacobian_double(jacobian_multiply(a, n//2)), a)
|
||||||
|
|
||||||
|
|
||||||
|
def fast_multiply(a, n):
|
||||||
|
return from_jacobian(jacobian_multiply(to_jacobian(a), n))
|
||||||
|
|
||||||
|
|
||||||
|
def fast_add(a, b):
|
||||||
|
return from_jacobian(jacobian_add(to_jacobian(a), to_jacobian(b)))
|
||||||
|
|
||||||
|
# Functions for handling pubkey and privkey formats
|
||||||
|
|
||||||
|
|
||||||
|
def get_pubkey_format(pub):
|
||||||
|
if is_python2:
|
||||||
|
two = '\x02'
|
||||||
|
three = '\x03'
|
||||||
|
four = '\x04'
|
||||||
|
else:
|
||||||
|
two = 2
|
||||||
|
three = 3
|
||||||
|
four = 4
|
||||||
|
|
||||||
|
if isinstance(pub, (tuple, list)): return 'decimal'
|
||||||
|
elif len(pub) == 65 and pub[0] == four: return 'bin'
|
||||||
|
elif len(pub) == 130 and pub[0:2] == '04': return 'hex'
|
||||||
|
elif len(pub) == 33 and pub[0] in [two, three]: return 'bin_compressed'
|
||||||
|
elif len(pub) == 66 and pub[0:2] in ['02', '03']: return 'hex_compressed'
|
||||||
|
elif len(pub) == 64: return 'bin_electrum'
|
||||||
|
elif len(pub) == 128: return 'hex_electrum'
|
||||||
|
else: raise Exception("Pubkey not in recognized format")
|
||||||
|
|
||||||
|
|
||||||
|
def encode_pubkey(pub, formt):
|
||||||
|
if not isinstance(pub, (tuple, list)):
|
||||||
|
pub = decode_pubkey(pub)
|
||||||
|
if formt == 'decimal': return pub
|
||||||
|
elif formt == 'bin': return b'\x04' + encode(pub[0], 256, 32) + encode(pub[1], 256, 32)
|
||||||
|
elif formt == 'bin_compressed':
|
||||||
|
return from_int_to_byte(2+(pub[1] % 2)) + encode(pub[0], 256, 32)
|
||||||
|
elif formt == 'hex': return '04' + encode(pub[0], 16, 64) + encode(pub[1], 16, 64)
|
||||||
|
elif formt == 'hex_compressed':
|
||||||
|
return '0'+str(2+(pub[1] % 2)) + encode(pub[0], 16, 64)
|
||||||
|
elif formt == 'bin_electrum': return encode(pub[0], 256, 32) + encode(pub[1], 256, 32)
|
||||||
|
elif formt == 'hex_electrum': return encode(pub[0], 16, 64) + encode(pub[1], 16, 64)
|
||||||
|
else: raise Exception("Invalid format!")
|
||||||
|
|
||||||
|
|
||||||
|
def decode_pubkey(pub, formt=None):
|
||||||
|
if not formt: formt = get_pubkey_format(pub)
|
||||||
|
if formt == 'decimal': return pub
|
||||||
|
elif formt == 'bin': return (decode(pub[1:33], 256), decode(pub[33:65], 256))
|
||||||
|
elif formt == 'bin_compressed':
|
||||||
|
x = decode(pub[1:33], 256)
|
||||||
|
beta = pow(int(x*x*x+A*x+B), int((P+1)//4), int(P))
|
||||||
|
y = (P-beta) if ((beta + from_byte_to_int(pub[0])) % 2) else beta
|
||||||
|
return (x, y)
|
||||||
|
elif formt == 'hex': return (decode(pub[2:66], 16), decode(pub[66:130], 16))
|
||||||
|
elif formt == 'hex_compressed':
|
||||||
|
return decode_pubkey(safe_from_hex(pub), 'bin_compressed')
|
||||||
|
elif formt == 'bin_electrum':
|
||||||
|
return (decode(pub[:32], 256), decode(pub[32:64], 256))
|
||||||
|
elif formt == 'hex_electrum':
|
||||||
|
return (decode(pub[:64], 16), decode(pub[64:128], 16))
|
||||||
|
else: raise Exception("Invalid format!")
|
||||||
|
|
||||||
|
def get_privkey_format(priv):
|
||||||
|
if isinstance(priv, int_types): return 'decimal'
|
||||||
|
elif len(priv) == 32: return 'bin'
|
||||||
|
elif len(priv) == 33: return 'bin_compressed'
|
||||||
|
elif len(priv) == 64: return 'hex'
|
||||||
|
elif len(priv) == 66: return 'hex_compressed'
|
||||||
|
else:
|
||||||
|
bin_p = b58check_to_bin(priv)
|
||||||
|
if len(bin_p) == 32: return 'wif'
|
||||||
|
elif len(bin_p) == 33: return 'wif_compressed'
|
||||||
|
else: raise Exception("WIF does not represent privkey")
|
||||||
|
|
||||||
|
def encode_privkey(priv, formt, vbyte=0):
|
||||||
|
if not isinstance(priv, int_types):
|
||||||
|
return encode_privkey(decode_privkey(priv), formt, vbyte)
|
||||||
|
if formt == 'decimal': return priv
|
||||||
|
elif formt == 'bin': return encode(priv, 256, 32)
|
||||||
|
elif formt == 'bin_compressed': return encode(priv, 256, 32)+b'\x01'
|
||||||
|
elif formt == 'hex': return encode(priv, 16, 64)
|
||||||
|
elif formt == 'hex_compressed': return encode(priv, 16, 64)+'01'
|
||||||
|
elif formt == 'wif':
|
||||||
|
return bin_to_b58check(encode(priv, 256, 32), 128+int(vbyte))
|
||||||
|
elif formt == 'wif_compressed':
|
||||||
|
return bin_to_b58check(encode(priv, 256, 32)+b'\x01', 128+int(vbyte))
|
||||||
|
else: raise Exception("Invalid format!")
|
||||||
|
|
||||||
|
def decode_privkey(priv,formt=None):
|
||||||
|
if not formt: formt = get_privkey_format(priv)
|
||||||
|
if formt == 'decimal': return priv
|
||||||
|
elif formt == 'bin': return decode(priv, 256)
|
||||||
|
elif formt == 'bin_compressed': return decode(priv[:32], 256)
|
||||||
|
elif formt == 'hex': return decode(priv, 16)
|
||||||
|
elif formt == 'hex_compressed': return decode(priv[:64], 16)
|
||||||
|
elif formt == 'wif': return decode(b58check_to_bin(priv),256)
|
||||||
|
elif formt == 'wif_compressed':
|
||||||
|
return decode(b58check_to_bin(priv)[:32],256)
|
||||||
|
else: raise Exception("WIF does not represent privkey")
|
||||||
|
|
||||||
|
def add_pubkeys(p1, p2):
|
||||||
|
f1, f2 = get_pubkey_format(p1), get_pubkey_format(p2)
|
||||||
|
return encode_pubkey(fast_add(decode_pubkey(p1, f1), decode_pubkey(p2, f2)), f1)
|
||||||
|
|
||||||
|
def add_privkeys(p1, p2):
|
||||||
|
f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
|
||||||
|
return encode_privkey((decode_privkey(p1, f1) + decode_privkey(p2, f2)) % N, f1)
|
||||||
|
|
||||||
|
def mul_privkeys(p1, p2):
|
||||||
|
f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
|
||||||
|
return encode_privkey((decode_privkey(p1, f1) * decode_privkey(p2, f2)) % N, f1)
|
||||||
|
|
||||||
|
def multiply(pubkey, privkey):
|
||||||
|
f1, f2 = get_pubkey_format(pubkey), get_privkey_format(privkey)
|
||||||
|
pubkey, privkey = decode_pubkey(pubkey, f1), decode_privkey(privkey, f2)
|
||||||
|
# http://safecurves.cr.yp.to/twist.html
|
||||||
|
if not isinf(pubkey) and (pubkey[0]**3+B-pubkey[1]*pubkey[1]) % P != 0:
|
||||||
|
raise Exception("Point not on curve")
|
||||||
|
return encode_pubkey(fast_multiply(pubkey, privkey), f1)
|
||||||
|
|
||||||
|
|
||||||
|
def divide(pubkey, privkey):
|
||||||
|
factor = inv(decode_privkey(privkey), N)
|
||||||
|
return multiply(pubkey, factor)
|
||||||
|
|
||||||
|
|
||||||
|
def compress(pubkey):
|
||||||
|
f = get_pubkey_format(pubkey)
|
||||||
|
if 'compressed' in f: return pubkey
|
||||||
|
elif f == 'bin': return encode_pubkey(decode_pubkey(pubkey, f), 'bin_compressed')
|
||||||
|
elif f == 'hex' or f == 'decimal':
|
||||||
|
return encode_pubkey(decode_pubkey(pubkey, f), 'hex_compressed')
|
||||||
|
|
||||||
|
|
||||||
|
def decompress(pubkey):
|
||||||
|
f = get_pubkey_format(pubkey)
|
||||||
|
if 'compressed' not in f: return pubkey
|
||||||
|
elif f == 'bin_compressed': return encode_pubkey(decode_pubkey(pubkey, f), 'bin')
|
||||||
|
elif f == 'hex_compressed' or f == 'decimal':
|
||||||
|
return encode_pubkey(decode_pubkey(pubkey, f), 'hex')
|
||||||
|
|
||||||
|
|
||||||
|
def privkey_to_pubkey(privkey):
|
||||||
|
f = get_privkey_format(privkey)
|
||||||
|
privkey = decode_privkey(privkey, f)
|
||||||
|
if privkey >= N:
|
||||||
|
raise Exception("Invalid privkey")
|
||||||
|
if f in ['bin', 'bin_compressed', 'hex', 'hex_compressed', 'decimal']:
|
||||||
|
return encode_pubkey(fast_multiply(G, privkey), f)
|
||||||
|
else:
|
||||||
|
return encode_pubkey(fast_multiply(G, privkey), f.replace('wif', 'hex'))
|
||||||
|
|
||||||
|
privtopub = privkey_to_pubkey
|
||||||
|
|
||||||
|
|
||||||
|
def privkey_to_address(priv, magicbyte=0):
|
||||||
|
return pubkey_to_address(privkey_to_pubkey(priv), magicbyte)
|
||||||
|
privtoaddr = privkey_to_address
|
||||||
|
|
||||||
|
|
||||||
|
def neg_pubkey(pubkey):
|
||||||
|
f = get_pubkey_format(pubkey)
|
||||||
|
pubkey = decode_pubkey(pubkey, f)
|
||||||
|
return encode_pubkey((pubkey[0], (P-pubkey[1]) % P), f)
|
||||||
|
|
||||||
|
|
||||||
|
def neg_privkey(privkey):
|
||||||
|
f = get_privkey_format(privkey)
|
||||||
|
privkey = decode_privkey(privkey, f)
|
||||||
|
return encode_privkey((N - privkey) % N, f)
|
||||||
|
|
||||||
|
def subtract_pubkeys(p1, p2):
|
||||||
|
f1, f2 = get_pubkey_format(p1), get_pubkey_format(p2)
|
||||||
|
k2 = decode_pubkey(p2, f2)
|
||||||
|
return encode_pubkey(fast_add(decode_pubkey(p1, f1), (k2[0], (P - k2[1]) % P)), f1)
|
||||||
|
|
||||||
|
|
||||||
|
def subtract_privkeys(p1, p2):
|
||||||
|
f1, f2 = get_privkey_format(p1), get_privkey_format(p2)
|
||||||
|
k2 = decode_privkey(p2, f2)
|
||||||
|
return encode_privkey((decode_privkey(p1, f1) - k2) % N, f1)
|
||||||
|
|
||||||
|
# Hashes
|
||||||
|
|
||||||
|
|
||||||
|
def bin_hash160(string):
|
||||||
|
intermed = hashlib.sha256(string).digest()
|
||||||
|
digest = ''
|
||||||
|
try:
|
||||||
|
digest = hashlib.new('ripemd160', intermed).digest()
|
||||||
|
except:
|
||||||
|
digest = RIPEMD160(intermed).digest()
|
||||||
|
return digest
|
||||||
|
|
||||||
|
|
||||||
|
def hash160(string):
|
||||||
|
return safe_hexlify(bin_hash160(string))
|
||||||
|
|
||||||
|
|
||||||
|
def bin_sha256(string):
|
||||||
|
binary_data = string if isinstance(string, bytes) else bytes(string, 'utf-8')
|
||||||
|
return hashlib.sha256(binary_data).digest()
|
||||||
|
|
||||||
|
def sha256(string):
|
||||||
|
return bytes_to_hex_string(bin_sha256(string))
|
||||||
|
|
||||||
|
|
||||||
|
def bin_ripemd160(string):
|
||||||
|
try:
|
||||||
|
digest = hashlib.new('ripemd160', string).digest()
|
||||||
|
except:
|
||||||
|
digest = RIPEMD160(string).digest()
|
||||||
|
return digest
|
||||||
|
|
||||||
|
|
||||||
|
def ripemd160(string):
|
||||||
|
return safe_hexlify(bin_ripemd160(string))
|
||||||
|
|
||||||
|
|
||||||
|
def bin_dbl_sha256(s):
|
||||||
|
bytes_to_hash = from_string_to_bytes(s)
|
||||||
|
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def dbl_sha256(string):
|
||||||
|
return safe_hexlify(bin_dbl_sha256(string))
|
||||||
|
|
||||||
|
|
||||||
|
def bin_slowsha(string):
|
||||||
|
string = from_string_to_bytes(string)
|
||||||
|
orig_input = string
|
||||||
|
for i in range(100000):
|
||||||
|
string = hashlib.sha256(string + orig_input).digest()
|
||||||
|
return string
|
||||||
|
|
||||||
|
|
||||||
|
def slowsha(string):
|
||||||
|
return safe_hexlify(bin_slowsha(string))
|
||||||
|
|
||||||
|
|
||||||
|
def hash_to_int(x):
|
||||||
|
if len(x) in [40, 64]:
|
||||||
|
return decode(x, 16)
|
||||||
|
return decode(x, 256)
|
||||||
|
|
||||||
|
|
||||||
|
def num_to_var_int(x):
|
||||||
|
x = int(x)
|
||||||
|
if x < 253: return from_int_to_byte(x)
|
||||||
|
elif x < 65536: return from_int_to_byte(253)+encode(x, 256, 2)[::-1]
|
||||||
|
elif x < 4294967296: return from_int_to_byte(254) + encode(x, 256, 4)[::-1]
|
||||||
|
else: return from_int_to_byte(255) + encode(x, 256, 8)[::-1]
|
||||||
|
|
||||||
|
|
||||||
|
# WTF, Electrum?
|
||||||
|
def electrum_sig_hash(message):
|
||||||
|
padded = b"\x18Bitcoin Signed Message:\n" + num_to_var_int(len(message)) + from_string_to_bytes(message)
|
||||||
|
return bin_dbl_sha256(padded)
|
||||||
|
|
||||||
|
|
||||||
|
def random_key():
|
||||||
|
# Gotta be secure after that java.SecureRandom fiasco...
|
||||||
|
entropy = random_string(32) \
|
||||||
|
+ str(random.randrange(2**256)) \
|
||||||
|
+ str(int(time.time() * 1000000))
|
||||||
|
return sha256(entropy)
|
||||||
|
|
||||||
|
|
||||||
|
def random_electrum_seed():
|
||||||
|
entropy = os.urandom(32) \
|
||||||
|
+ str(random.randrange(2**256)) \
|
||||||
|
+ str(int(time.time() * 1000000))
|
||||||
|
return sha256(entropy)[:32]
|
||||||
|
|
||||||
|
# Encodings
|
||||||
|
|
||||||
|
def b58check_to_bin(inp):
|
||||||
|
leadingzbytes = len(re.match('^1*', inp).group(0))
|
||||||
|
data = b'\x00' * leadingzbytes + changebase(inp, 58, 256)
|
||||||
|
assert bin_dbl_sha256(data[:-4])[:4] == data[-4:]
|
||||||
|
return data[1:-4]
|
||||||
|
|
||||||
|
|
||||||
|
def get_version_byte(inp):
|
||||||
|
leadingzbytes = len(re.match('^1*', inp).group(0))
|
||||||
|
data = b'\x00' * leadingzbytes + changebase(inp, 58, 256)
|
||||||
|
assert bin_dbl_sha256(data[:-4])[:4] == data[-4:]
|
||||||
|
return ord(data[0])
|
||||||
|
|
||||||
|
|
||||||
|
def hex_to_b58check(inp, magicbyte=0):
|
||||||
|
return bin_to_b58check(binascii.unhexlify(inp), magicbyte)
|
||||||
|
|
||||||
|
|
||||||
|
def b58check_to_hex(inp):
|
||||||
|
return safe_hexlify(b58check_to_bin(inp))
|
||||||
|
|
||||||
|
|
||||||
|
def pubkey_to_address(pubkey, magicbyte=0):
|
||||||
|
if isinstance(pubkey, (list, tuple)):
|
||||||
|
pubkey = encode_pubkey(pubkey, 'bin')
|
||||||
|
if len(pubkey) in [66, 130]:
|
||||||
|
return bin_to_b58check(
|
||||||
|
bin_hash160(binascii.unhexlify(pubkey)), magicbyte)
|
||||||
|
return bin_to_b58check(bin_hash160(pubkey), magicbyte)
|
||||||
|
|
||||||
|
pubtoaddr = pubkey_to_address
|
||||||
|
|
||||||
|
|
||||||
|
def is_privkey(priv):
|
||||||
|
try:
|
||||||
|
get_privkey_format(priv)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_pubkey(pubkey):
|
||||||
|
try:
|
||||||
|
get_pubkey_format(pubkey)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_address(addr):
|
||||||
|
ADDR_RE = re.compile("^[123mn][a-km-zA-HJ-NP-Z0-9]{26,33}$")
|
||||||
|
return bool(ADDR_RE.match(addr))
|
||||||
|
|
||||||
|
|
||||||
|
# EDCSA
|
||||||
|
|
||||||
|
|
||||||
|
def encode_sig(v, r, s):
|
||||||
|
vb, rb, sb = from_int_to_byte(v), encode(r, 256), encode(s, 256)
|
||||||
|
|
||||||
|
result = base64.b64encode(vb+b'\x00'*(32-len(rb))+rb+b'\x00'*(32-len(sb))+sb)
|
||||||
|
return result if is_python2 else str(result, 'utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def decode_sig(sig):
|
||||||
|
bytez = base64.b64decode(sig)
|
||||||
|
return from_byte_to_int(bytez[0]), decode(bytez[1:33], 256), decode(bytez[33:], 256)
|
||||||
|
|
||||||
|
# https://tools.ietf.org/html/rfc6979#section-3.2
|
||||||
|
|
||||||
|
|
||||||
|
def deterministic_generate_k(msghash, priv):
|
||||||
|
v = b'\x01' * 32
|
||||||
|
k = b'\x00' * 32
|
||||||
|
priv = encode_privkey(priv, 'bin')
|
||||||
|
msghash = encode(hash_to_int(msghash), 256, 32)
|
||||||
|
k = hmac.new(k, v+b'\x00'+priv+msghash, hashlib.sha256).digest()
|
||||||
|
v = hmac.new(k, v, hashlib.sha256).digest()
|
||||||
|
k = hmac.new(k, v+b'\x01'+priv+msghash, hashlib.sha256).digest()
|
||||||
|
v = hmac.new(k, v, hashlib.sha256).digest()
|
||||||
|
return decode(hmac.new(k, v, hashlib.sha256).digest(), 256)
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_raw_sign(msghash, priv):
|
||||||
|
|
||||||
|
z = hash_to_int(msghash)
|
||||||
|
k = deterministic_generate_k(msghash, priv)
|
||||||
|
|
||||||
|
r, y = fast_multiply(G, k)
|
||||||
|
s = inv(k, N) * (z + r*decode_privkey(priv)) % N
|
||||||
|
|
||||||
|
v, r, s = 27+((y % 2) ^ (0 if s * 2 < N else 1)), r, s if s * 2 < N else N - s
|
||||||
|
if 'compressed' in get_privkey_format(priv):
|
||||||
|
v += 4
|
||||||
|
return v, r, s
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_sign(msg, priv):
|
||||||
|
v, r, s = ecdsa_raw_sign(electrum_sig_hash(msg), priv)
|
||||||
|
sig = encode_sig(v, r, s)
|
||||||
|
assert ecdsa_verify(msg, sig,
|
||||||
|
privtopub(priv)), "Bad Sig!\t %s\nv = %d\n,r = %d\ns = %d" % (sig, v, r, s)
|
||||||
|
return sig
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_raw_verify(msghash, vrs, pub):
|
||||||
|
v, r, s = vrs
|
||||||
|
if not (27 <= v <= 34):
|
||||||
|
return False
|
||||||
|
|
||||||
|
w = inv(s, N)
|
||||||
|
z = hash_to_int(msghash)
|
||||||
|
|
||||||
|
u1, u2 = z*w % N, r*w % N
|
||||||
|
x, y = fast_add(fast_multiply(G, u1), fast_multiply(decode_pubkey(pub), u2))
|
||||||
|
return bool(r == x and (r % N) and (s % N))
|
||||||
|
|
||||||
|
|
||||||
|
# For BitcoinCore, (msg = addr or msg = "") be default
|
||||||
|
def ecdsa_verify_addr(msg, sig, addr):
|
||||||
|
assert is_address(addr)
|
||||||
|
Q = ecdsa_recover(msg, sig)
|
||||||
|
magic = get_version_byte(addr)
|
||||||
|
return (addr == pubtoaddr(Q, int(magic))) or (addr == pubtoaddr(compress(Q), int(magic)))
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_verify(msg, sig, pub):
|
||||||
|
if is_address(pub):
|
||||||
|
return ecdsa_verify_addr(msg, sig, pub)
|
||||||
|
return ecdsa_raw_verify(electrum_sig_hash(msg), decode_sig(sig), pub)
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_raw_recover(msghash, vrs):
|
||||||
|
v, r, s = vrs
|
||||||
|
if not (27 <= v <= 34):
|
||||||
|
raise ValueError("%d must in range 27-31" % v)
|
||||||
|
x = r
|
||||||
|
xcubedaxb = (x*x*x+A*x+B) % P
|
||||||
|
beta = pow(xcubedaxb, (P+1)//4, P)
|
||||||
|
y = beta if v % 2 ^ beta % 2 else (P - beta)
|
||||||
|
# If xcubedaxb is not a quadratic residue, then r cannot be the x coord
|
||||||
|
# for a point on the curve, and so the sig is invalid
|
||||||
|
if (xcubedaxb - y*y) % P != 0 or not (r % N) or not (s % N):
|
||||||
|
return False
|
||||||
|
z = hash_to_int(msghash)
|
||||||
|
Gz = jacobian_multiply((Gx, Gy, 1), (N - z) % N)
|
||||||
|
XY = jacobian_multiply((x, y, 1), s)
|
||||||
|
Qr = jacobian_add(Gz, XY)
|
||||||
|
Q = jacobian_multiply(Qr, inv(r, N))
|
||||||
|
Q = from_jacobian(Q)
|
||||||
|
|
||||||
|
# if ecdsa_raw_verify(msghash, vrs, Q):
|
||||||
|
return Q
|
||||||
|
# return False
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_recover(msg, sig):
|
||||||
|
v,r,s = decode_sig(sig)
|
||||||
|
Q = ecdsa_raw_recover(electrum_sig_hash(msg), (v,r,s))
|
||||||
|
return encode_pubkey(Q, 'hex_compressed') if v >= 31 else encode_pubkey(Q, 'hex')
|
127
src/lib/pybitcointools/mnemonic.py
Normal file
127
src/lib/pybitcointools/mnemonic.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import hashlib
|
||||||
|
import os.path
|
||||||
|
import binascii
|
||||||
|
import random
|
||||||
|
from bisect import bisect_left
|
||||||
|
|
||||||
|
wordlist_english=list(open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'english.txt'),'r'))
|
||||||
|
|
||||||
|
def eint_to_bytes(entint,entbits):
|
||||||
|
a=hex(entint)[2:].rstrip('L').zfill(32)
|
||||||
|
print(a)
|
||||||
|
return binascii.unhexlify(a)
|
||||||
|
|
||||||
|
def mnemonic_int_to_words(mint,mint_num_words,wordlist=wordlist_english):
|
||||||
|
backwords=[wordlist[(mint >> (11*x)) & 0x7FF].strip() for x in range(mint_num_words)]
|
||||||
|
return backwords[::-1]
|
||||||
|
|
||||||
|
def entropy_cs(entbytes):
|
||||||
|
entropy_size=8*len(entbytes)
|
||||||
|
checksum_size=entropy_size//32
|
||||||
|
hd=hashlib.sha256(entbytes).hexdigest()
|
||||||
|
csint=int(hd,16) >> (256-checksum_size)
|
||||||
|
return csint,checksum_size
|
||||||
|
|
||||||
|
def entropy_to_words(entbytes,wordlist=wordlist_english):
|
||||||
|
if(len(entbytes) < 4 or len(entbytes) % 4 != 0):
|
||||||
|
raise ValueError("The size of the entropy must be a multiple of 4 bytes (multiple of 32 bits)")
|
||||||
|
entropy_size=8*len(entbytes)
|
||||||
|
csint,checksum_size = entropy_cs(entbytes)
|
||||||
|
entint=int(binascii.hexlify(entbytes),16)
|
||||||
|
mint=(entint << checksum_size) | csint
|
||||||
|
mint_num_words=(entropy_size+checksum_size)//11
|
||||||
|
|
||||||
|
return mnemonic_int_to_words(mint,mint_num_words,wordlist)
|
||||||
|
|
||||||
|
def words_bisect(word,wordlist=wordlist_english):
|
||||||
|
lo=bisect_left(wordlist,word)
|
||||||
|
hi=len(wordlist)-bisect_left(wordlist[:lo:-1],word)
|
||||||
|
|
||||||
|
return lo,hi
|
||||||
|
|
||||||
|
def words_split(wordstr,wordlist=wordlist_english):
|
||||||
|
def popword(wordstr,wordlist):
|
||||||
|
for fwl in range(1,9):
|
||||||
|
w=wordstr[:fwl].strip()
|
||||||
|
lo,hi=words_bisect(w,wordlist)
|
||||||
|
if(hi-lo == 1):
|
||||||
|
return w,wordstr[fwl:].lstrip()
|
||||||
|
wordlist=wordlist[lo:hi]
|
||||||
|
raise Exception("Wordstr %s not found in list" %(w))
|
||||||
|
|
||||||
|
words=[]
|
||||||
|
tail=wordstr
|
||||||
|
while(len(tail)):
|
||||||
|
head,tail=popword(tail,wordlist)
|
||||||
|
words.append(head)
|
||||||
|
return words
|
||||||
|
|
||||||
|
def words_to_mnemonic_int(words,wordlist=wordlist_english):
|
||||||
|
if(isinstance(words,str)):
|
||||||
|
words=words_split(words,wordlist)
|
||||||
|
return sum([wordlist.index(w) << (11*x) for x,w in enumerate(words[::-1])])
|
||||||
|
|
||||||
|
def words_verify(words,wordlist=wordlist_english):
|
||||||
|
if(isinstance(words,str)):
|
||||||
|
words=words_split(words,wordlist)
|
||||||
|
|
||||||
|
mint = words_to_mnemonic_int(words,wordlist)
|
||||||
|
mint_bits=len(words)*11
|
||||||
|
cs_bits=mint_bits//32
|
||||||
|
entropy_bits=mint_bits-cs_bits
|
||||||
|
eint=mint >> cs_bits
|
||||||
|
csint=mint & ((1 << cs_bits)-1)
|
||||||
|
ebytes=_eint_to_bytes(eint,entropy_bits)
|
||||||
|
return csint == entropy_cs(ebytes)
|
||||||
|
|
||||||
|
def mnemonic_to_seed(mnemonic_phrase,passphrase=b''):
|
||||||
|
try:
|
||||||
|
from hashlib import pbkdf2_hmac
|
||||||
|
def pbkdf2_hmac_sha256(password,salt,iters=2048):
|
||||||
|
return pbkdf2_hmac(hash_name='sha512',password=password,salt=salt,iterations=iters)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
from Crypto.Protocol.KDF import PBKDF2
|
||||||
|
from Crypto.Hash import SHA512,HMAC
|
||||||
|
|
||||||
|
def pbkdf2_hmac_sha256(password,salt,iters=2048):
|
||||||
|
return PBKDF2(password=password,salt=salt,dkLen=64,count=iters,prf=lambda p,s: HMAC.new(p,s,SHA512).digest())
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
|
||||||
|
from pbkdf2 import PBKDF2
|
||||||
|
import hmac
|
||||||
|
def pbkdf2_hmac_sha256(password,salt,iters=2048):
|
||||||
|
return PBKDF2(password,salt, iterations=iters, macmodule=hmac, digestmodule=hashlib.sha512).read(64)
|
||||||
|
except:
|
||||||
|
raise RuntimeError("No implementation of pbkdf2 was found!")
|
||||||
|
|
||||||
|
return pbkdf2_hmac_sha256(password=mnemonic_phrase,salt=b'mnemonic'+passphrase)
|
||||||
|
|
||||||
|
def words_mine(prefix,entbits,satisfunction,wordlist=wordlist_english,randombits=random.getrandbits):
|
||||||
|
prefix_bits=len(prefix)*11
|
||||||
|
mine_bits=entbits-prefix_bits
|
||||||
|
pint=words_to_mnemonic_int(prefix,wordlist)
|
||||||
|
pint<<=mine_bits
|
||||||
|
dint=randombits(mine_bits)
|
||||||
|
count=0
|
||||||
|
while(not satisfunction(entropy_to_words(eint_to_bytes(pint+dint,entbits)))):
|
||||||
|
dint=randombits(mine_bits)
|
||||||
|
if((count & 0xFFFF) == 0):
|
||||||
|
print("Searched %f percent of the space" % (float(count)/float(1 << mine_bits)))
|
||||||
|
|
||||||
|
return entropy_to_words(eint_to_bytes(pint+dint,entbits))
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
import json
|
||||||
|
testvectors=json.load(open('vectors.json','r'))
|
||||||
|
passed=True
|
||||||
|
for v in testvectors['english']:
|
||||||
|
ebytes=binascii.unhexlify(v[0])
|
||||||
|
w=' '.join(entropy_to_words(ebytes))
|
||||||
|
seed=mnemonic_to_seed(w,passphrase='TREZOR')
|
||||||
|
passed = passed and w==v[1]
|
||||||
|
passed = passed and binascii.hexlify(seed)==v[2]
|
||||||
|
print("Tests %s." % ("Passed" if passed else "Failed"))
|
||||||
|
|
||||||
|
|
98
src/lib/pybitcointools/py2specials.py
Normal file
98
src/lib/pybitcointools/py2specials.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import sys, re
|
||||||
|
import binascii
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info.major == 2:
|
||||||
|
string_types = (str, unicode)
|
||||||
|
string_or_bytes_types = string_types
|
||||||
|
int_types = (int, float, long)
|
||||||
|
|
||||||
|
# Base switching
|
||||||
|
code_strings = {
|
||||||
|
2: '01',
|
||||||
|
10: '0123456789',
|
||||||
|
16: '0123456789abcdef',
|
||||||
|
32: 'abcdefghijklmnopqrstuvwxyz234567',
|
||||||
|
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
|
||||||
|
256: ''.join([chr(x) for x in range(256)])
|
||||||
|
}
|
||||||
|
|
||||||
|
def bin_dbl_sha256(s):
|
||||||
|
bytes_to_hash = from_string_to_bytes(s)
|
||||||
|
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
|
||||||
|
|
||||||
|
def lpad(msg, symbol, length):
|
||||||
|
if len(msg) >= length:
|
||||||
|
return msg
|
||||||
|
return symbol * (length - len(msg)) + msg
|
||||||
|
|
||||||
|
def get_code_string(base):
|
||||||
|
if base in code_strings:
|
||||||
|
return code_strings[base]
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid base!")
|
||||||
|
|
||||||
|
def changebase(string, frm, to, minlen=0):
|
||||||
|
if frm == to:
|
||||||
|
return lpad(string, get_code_string(frm)[0], minlen)
|
||||||
|
return encode(decode(string, frm), to, minlen)
|
||||||
|
|
||||||
|
def bin_to_b58check(inp, magicbyte=0):
|
||||||
|
if magicbyte == 0:
|
||||||
|
inp = '\x00' + inp
|
||||||
|
while magicbyte > 0:
|
||||||
|
inp = chr(int(magicbyte % 256)) + inp
|
||||||
|
magicbyte //= 256
|
||||||
|
leadingzbytes = len(re.match('^\x00*', inp).group(0))
|
||||||
|
checksum = bin_dbl_sha256(inp)[:4]
|
||||||
|
return '1' * leadingzbytes + changebase(inp+checksum, 256, 58)
|
||||||
|
|
||||||
|
def bytes_to_hex_string(b):
|
||||||
|
return b.encode('hex')
|
||||||
|
|
||||||
|
def safe_from_hex(s):
|
||||||
|
return s.decode('hex')
|
||||||
|
|
||||||
|
def from_int_representation_to_bytes(a):
|
||||||
|
return str(a)
|
||||||
|
|
||||||
|
def from_int_to_byte(a):
|
||||||
|
return chr(a)
|
||||||
|
|
||||||
|
def from_byte_to_int(a):
|
||||||
|
return ord(a)
|
||||||
|
|
||||||
|
def from_bytes_to_string(s):
|
||||||
|
return s
|
||||||
|
|
||||||
|
def from_string_to_bytes(a):
|
||||||
|
return a
|
||||||
|
|
||||||
|
def safe_hexlify(a):
|
||||||
|
return binascii.hexlify(a)
|
||||||
|
|
||||||
|
def encode(val, base, minlen=0):
|
||||||
|
base, minlen = int(base), int(minlen)
|
||||||
|
code_string = get_code_string(base)
|
||||||
|
result = ""
|
||||||
|
while val > 0:
|
||||||
|
result = code_string[val % base] + result
|
||||||
|
val //= base
|
||||||
|
return code_string[0] * max(minlen - len(result), 0) + result
|
||||||
|
|
||||||
|
def decode(string, base):
|
||||||
|
base = int(base)
|
||||||
|
code_string = get_code_string(base)
|
||||||
|
result = 0
|
||||||
|
if base == 16:
|
||||||
|
string = string.lower()
|
||||||
|
while len(string) > 0:
|
||||||
|
result *= base
|
||||||
|
result += code_string.find(string[0])
|
||||||
|
string = string[1:]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def random_string(x):
|
||||||
|
return os.urandom(x)
|
123
src/lib/pybitcointools/py3specials.py
Normal file
123
src/lib/pybitcointools/py3specials.py
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import sys, os
|
||||||
|
import binascii
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info.major == 3:
|
||||||
|
string_types = (str)
|
||||||
|
string_or_bytes_types = (str, bytes)
|
||||||
|
int_types = (int, float)
|
||||||
|
# Base switching
|
||||||
|
code_strings = {
|
||||||
|
2: '01',
|
||||||
|
10: '0123456789',
|
||||||
|
16: '0123456789abcdef',
|
||||||
|
32: 'abcdefghijklmnopqrstuvwxyz234567',
|
||||||
|
58: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
|
||||||
|
256: ''.join([chr(x) for x in range(256)])
|
||||||
|
}
|
||||||
|
|
||||||
|
def bin_dbl_sha256(s):
|
||||||
|
bytes_to_hash = from_string_to_bytes(s)
|
||||||
|
return hashlib.sha256(hashlib.sha256(bytes_to_hash).digest()).digest()
|
||||||
|
|
||||||
|
def lpad(msg, symbol, length):
|
||||||
|
if len(msg) >= length:
|
||||||
|
return msg
|
||||||
|
return symbol * (length - len(msg)) + msg
|
||||||
|
|
||||||
|
def get_code_string(base):
|
||||||
|
if base in code_strings:
|
||||||
|
return code_strings[base]
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid base!")
|
||||||
|
|
||||||
|
def changebase(string, frm, to, minlen=0):
|
||||||
|
if frm == to:
|
||||||
|
return lpad(string, get_code_string(frm)[0], minlen)
|
||||||
|
return encode(decode(string, frm), to, minlen)
|
||||||
|
|
||||||
|
def bin_to_b58check(inp, magicbyte=0):
|
||||||
|
if magicbyte == 0:
|
||||||
|
inp = from_int_to_byte(0) + inp
|
||||||
|
while magicbyte > 0:
|
||||||
|
inp = from_int_to_byte(magicbyte % 256) + inp
|
||||||
|
magicbyte //= 256
|
||||||
|
|
||||||
|
leadingzbytes = 0
|
||||||
|
for x in inp:
|
||||||
|
if x != 0:
|
||||||
|
break
|
||||||
|
leadingzbytes += 1
|
||||||
|
|
||||||
|
checksum = bin_dbl_sha256(inp)[:4]
|
||||||
|
return '1' * leadingzbytes + changebase(inp+checksum, 256, 58)
|
||||||
|
|
||||||
|
def bytes_to_hex_string(b):
|
||||||
|
if isinstance(b, str):
|
||||||
|
return b
|
||||||
|
|
||||||
|
return ''.join('{:02x}'.format(y) for y in b)
|
||||||
|
|
||||||
|
def safe_from_hex(s):
|
||||||
|
return bytes.fromhex(s)
|
||||||
|
|
||||||
|
def from_int_representation_to_bytes(a):
|
||||||
|
return bytes(str(a), 'utf-8')
|
||||||
|
|
||||||
|
def from_int_to_byte(a):
|
||||||
|
return bytes([a])
|
||||||
|
|
||||||
|
def from_byte_to_int(a):
|
||||||
|
return a
|
||||||
|
|
||||||
|
def from_string_to_bytes(a):
|
||||||
|
return a if isinstance(a, bytes) else bytes(a, 'utf-8')
|
||||||
|
|
||||||
|
def safe_hexlify(a):
|
||||||
|
return str(binascii.hexlify(a), 'utf-8')
|
||||||
|
|
||||||
|
def encode(val, base, minlen=0):
|
||||||
|
base, minlen = int(base), int(minlen)
|
||||||
|
code_string = get_code_string(base)
|
||||||
|
result_bytes = bytes()
|
||||||
|
while val > 0:
|
||||||
|
curcode = code_string[val % base]
|
||||||
|
result_bytes = bytes([ord(curcode)]) + result_bytes
|
||||||
|
val //= base
|
||||||
|
|
||||||
|
pad_size = minlen - len(result_bytes)
|
||||||
|
|
||||||
|
padding_element = b'\x00' if base == 256 else b'1' \
|
||||||
|
if base == 58 else b'0'
|
||||||
|
if (pad_size > 0):
|
||||||
|
result_bytes = padding_element*pad_size + result_bytes
|
||||||
|
|
||||||
|
result_string = ''.join([chr(y) for y in result_bytes])
|
||||||
|
result = result_bytes if base == 256 else result_string
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def decode(string, base):
|
||||||
|
if base == 256 and isinstance(string, str):
|
||||||
|
string = bytes(bytearray.fromhex(string))
|
||||||
|
base = int(base)
|
||||||
|
code_string = get_code_string(base)
|
||||||
|
result = 0
|
||||||
|
if base == 256:
|
||||||
|
def extract(d, cs):
|
||||||
|
return d
|
||||||
|
else:
|
||||||
|
def extract(d, cs):
|
||||||
|
return cs.find(d if isinstance(d, str) else chr(d))
|
||||||
|
|
||||||
|
if base == 16:
|
||||||
|
string = string.lower()
|
||||||
|
while len(string) > 0:
|
||||||
|
result *= base
|
||||||
|
result += extract(string[0], code_string)
|
||||||
|
string = string[1:]
|
||||||
|
return result
|
||||||
|
|
||||||
|
def random_string(x):
|
||||||
|
return str(os.urandom(x))
|
414
src/lib/pybitcointools/ripemd.py
Normal file
414
src/lib/pybitcointools/ripemd.py
Normal file
|
@ -0,0 +1,414 @@
|
||||||
|
## ripemd.py - pure Python implementation of the RIPEMD-160 algorithm.
|
||||||
|
## Bjorn Edstrom <be@bjrn.se> 16 december 2007.
|
||||||
|
##
|
||||||
|
## Copyrights
|
||||||
|
## ==========
|
||||||
|
##
|
||||||
|
## This code is a derived from an implementation by Markus Friedl which is
|
||||||
|
## subject to the following license. This Python implementation is not
|
||||||
|
## subject to any other license.
|
||||||
|
##
|
||||||
|
##/*
|
||||||
|
## * Copyright (c) 2001 Markus Friedl. All rights reserved.
|
||||||
|
## *
|
||||||
|
## * Redistribution and use in source and binary forms, with or without
|
||||||
|
## * modification, are permitted provided that the following conditions
|
||||||
|
## * are met:
|
||||||
|
## * 1. Redistributions of source code must retain the above copyright
|
||||||
|
## * notice, this list of conditions and the following disclaimer.
|
||||||
|
## * 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
## * notice, this list of conditions and the following disclaimer in the
|
||||||
|
## * documentation and/or other materials provided with the distribution.
|
||||||
|
## *
|
||||||
|
## * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
## * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
## * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
## * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
## * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
## * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
## * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
## * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
## * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
## * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
## */
|
||||||
|
##/*
|
||||||
|
## * Preneel, Bosselaers, Dobbertin, "The Cryptographic Hash Function RIPEMD-160",
|
||||||
|
## * RSA Laboratories, CryptoBytes, Volume 3, Number 2, Autumn 1997,
|
||||||
|
## * ftp://ftp.rsasecurity.com/pub/cryptobytes/crypto3n2.pdf
|
||||||
|
## */
|
||||||
|
|
||||||
|
try:
|
||||||
|
import psyco
|
||||||
|
psyco.full()
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
is_python2 = sys.version_info.major == 2
|
||||||
|
#block_size = 1
|
||||||
|
digest_size = 20
|
||||||
|
digestsize = 20
|
||||||
|
|
||||||
|
try:
|
||||||
|
range = xrange
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class RIPEMD160:
|
||||||
|
"""Return a new RIPEMD160 object. An optional string argument
|
||||||
|
may be provided; if present, this string will be automatically
|
||||||
|
hashed."""
|
||||||
|
|
||||||
|
def __init__(self, arg=None):
|
||||||
|
self.ctx = RMDContext()
|
||||||
|
if arg:
|
||||||
|
self.update(arg)
|
||||||
|
self.dig = None
|
||||||
|
|
||||||
|
def update(self, arg):
|
||||||
|
"""update(arg)"""
|
||||||
|
RMD160Update(self.ctx, arg, len(arg))
|
||||||
|
self.dig = None
|
||||||
|
|
||||||
|
def digest(self):
|
||||||
|
"""digest()"""
|
||||||
|
if self.dig:
|
||||||
|
return self.dig
|
||||||
|
ctx = self.ctx.copy()
|
||||||
|
self.dig = RMD160Final(self.ctx)
|
||||||
|
self.ctx = ctx
|
||||||
|
return self.dig
|
||||||
|
|
||||||
|
def hexdigest(self):
|
||||||
|
"""hexdigest()"""
|
||||||
|
dig = self.digest()
|
||||||
|
hex_digest = ''
|
||||||
|
for d in dig:
|
||||||
|
if (is_python2):
|
||||||
|
hex_digest += '%02x' % ord(d)
|
||||||
|
else:
|
||||||
|
hex_digest += '%02x' % d
|
||||||
|
return hex_digest
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
"""copy()"""
|
||||||
|
import copy
|
||||||
|
return copy.deepcopy(self)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def new(arg=None):
|
||||||
|
"""Return a new RIPEMD160 object. An optional string argument
|
||||||
|
may be provided; if present, this string will be automatically
|
||||||
|
hashed."""
|
||||||
|
return RIPEMD160(arg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Private.
|
||||||
|
#
|
||||||
|
|
||||||
|
class RMDContext:
|
||||||
|
def __init__(self):
|
||||||
|
self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE,
|
||||||
|
0x10325476, 0xC3D2E1F0] # uint32
|
||||||
|
self.count = 0 # uint64
|
||||||
|
self.buffer = [0]*64 # uchar
|
||||||
|
def copy(self):
|
||||||
|
ctx = RMDContext()
|
||||||
|
ctx.state = self.state[:]
|
||||||
|
ctx.count = self.count
|
||||||
|
ctx.buffer = self.buffer[:]
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
K0 = 0x00000000
|
||||||
|
K1 = 0x5A827999
|
||||||
|
K2 = 0x6ED9EBA1
|
||||||
|
K3 = 0x8F1BBCDC
|
||||||
|
K4 = 0xA953FD4E
|
||||||
|
|
||||||
|
KK0 = 0x50A28BE6
|
||||||
|
KK1 = 0x5C4DD124
|
||||||
|
KK2 = 0x6D703EF3
|
||||||
|
KK3 = 0x7A6D76E9
|
||||||
|
KK4 = 0x00000000
|
||||||
|
|
||||||
|
def ROL(n, x):
|
||||||
|
return ((x << n) & 0xffffffff) | (x >> (32 - n))
|
||||||
|
|
||||||
|
def F0(x, y, z):
|
||||||
|
return x ^ y ^ z
|
||||||
|
|
||||||
|
def F1(x, y, z):
|
||||||
|
return (x & y) | (((~x) % 0x100000000) & z)
|
||||||
|
|
||||||
|
def F2(x, y, z):
|
||||||
|
return (x | ((~y) % 0x100000000)) ^ z
|
||||||
|
|
||||||
|
def F3(x, y, z):
|
||||||
|
return (x & z) | (((~z) % 0x100000000) & y)
|
||||||
|
|
||||||
|
def F4(x, y, z):
|
||||||
|
return x ^ (y | ((~z) % 0x100000000))
|
||||||
|
|
||||||
|
def R(a, b, c, d, e, Fj, Kj, sj, rj, X):
|
||||||
|
a = ROL(sj, (a + Fj(b, c, d) + X[rj] + Kj) % 0x100000000) + e
|
||||||
|
c = ROL(10, c)
|
||||||
|
return a % 0x100000000, c
|
||||||
|
|
||||||
|
PADDING = [0x80] + [0]*63
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import struct
|
||||||
|
|
||||||
|
def RMD160Transform(state, block): #uint32 state[5], uchar block[64]
|
||||||
|
x = [0]*16
|
||||||
|
if sys.byteorder == 'little':
|
||||||
|
if is_python2:
|
||||||
|
x = struct.unpack('<16L', ''.join([chr(x) for x in block[0:64]]))
|
||||||
|
else:
|
||||||
|
x = struct.unpack('<16L', bytes(block[0:64]))
|
||||||
|
else:
|
||||||
|
raise "Error!!"
|
||||||
|
a = state[0]
|
||||||
|
b = state[1]
|
||||||
|
c = state[2]
|
||||||
|
d = state[3]
|
||||||
|
e = state[4]
|
||||||
|
|
||||||
|
#/* Round 1 */
|
||||||
|
a, c = R(a, b, c, d, e, F0, K0, 11, 0, x);
|
||||||
|
e, b = R(e, a, b, c, d, F0, K0, 14, 1, x);
|
||||||
|
d, a = R(d, e, a, b, c, F0, K0, 15, 2, x);
|
||||||
|
c, e = R(c, d, e, a, b, F0, K0, 12, 3, x);
|
||||||
|
b, d = R(b, c, d, e, a, F0, K0, 5, 4, x);
|
||||||
|
a, c = R(a, b, c, d, e, F0, K0, 8, 5, x);
|
||||||
|
e, b = R(e, a, b, c, d, F0, K0, 7, 6, x);
|
||||||
|
d, a = R(d, e, a, b, c, F0, K0, 9, 7, x);
|
||||||
|
c, e = R(c, d, e, a, b, F0, K0, 11, 8, x);
|
||||||
|
b, d = R(b, c, d, e, a, F0, K0, 13, 9, x);
|
||||||
|
a, c = R(a, b, c, d, e, F0, K0, 14, 10, x);
|
||||||
|
e, b = R(e, a, b, c, d, F0, K0, 15, 11, x);
|
||||||
|
d, a = R(d, e, a, b, c, F0, K0, 6, 12, x);
|
||||||
|
c, e = R(c, d, e, a, b, F0, K0, 7, 13, x);
|
||||||
|
b, d = R(b, c, d, e, a, F0, K0, 9, 14, x);
|
||||||
|
a, c = R(a, b, c, d, e, F0, K0, 8, 15, x); #/* #15 */
|
||||||
|
#/* Round 2 */
|
||||||
|
e, b = R(e, a, b, c, d, F1, K1, 7, 7, x);
|
||||||
|
d, a = R(d, e, a, b, c, F1, K1, 6, 4, x);
|
||||||
|
c, e = R(c, d, e, a, b, F1, K1, 8, 13, x);
|
||||||
|
b, d = R(b, c, d, e, a, F1, K1, 13, 1, x);
|
||||||
|
a, c = R(a, b, c, d, e, F1, K1, 11, 10, x);
|
||||||
|
e, b = R(e, a, b, c, d, F1, K1, 9, 6, x);
|
||||||
|
d, a = R(d, e, a, b, c, F1, K1, 7, 15, x);
|
||||||
|
c, e = R(c, d, e, a, b, F1, K1, 15, 3, x);
|
||||||
|
b, d = R(b, c, d, e, a, F1, K1, 7, 12, x);
|
||||||
|
a, c = R(a, b, c, d, e, F1, K1, 12, 0, x);
|
||||||
|
e, b = R(e, a, b, c, d, F1, K1, 15, 9, x);
|
||||||
|
d, a = R(d, e, a, b, c, F1, K1, 9, 5, x);
|
||||||
|
c, e = R(c, d, e, a, b, F1, K1, 11, 2, x);
|
||||||
|
b, d = R(b, c, d, e, a, F1, K1, 7, 14, x);
|
||||||
|
a, c = R(a, b, c, d, e, F1, K1, 13, 11, x);
|
||||||
|
e, b = R(e, a, b, c, d, F1, K1, 12, 8, x); #/* #31 */
|
||||||
|
#/* Round 3 */
|
||||||
|
d, a = R(d, e, a, b, c, F2, K2, 11, 3, x);
|
||||||
|
c, e = R(c, d, e, a, b, F2, K2, 13, 10, x);
|
||||||
|
b, d = R(b, c, d, e, a, F2, K2, 6, 14, x);
|
||||||
|
a, c = R(a, b, c, d, e, F2, K2, 7, 4, x);
|
||||||
|
e, b = R(e, a, b, c, d, F2, K2, 14, 9, x);
|
||||||
|
d, a = R(d, e, a, b, c, F2, K2, 9, 15, x);
|
||||||
|
c, e = R(c, d, e, a, b, F2, K2, 13, 8, x);
|
||||||
|
b, d = R(b, c, d, e, a, F2, K2, 15, 1, x);
|
||||||
|
a, c = R(a, b, c, d, e, F2, K2, 14, 2, x);
|
||||||
|
e, b = R(e, a, b, c, d, F2, K2, 8, 7, x);
|
||||||
|
d, a = R(d, e, a, b, c, F2, K2, 13, 0, x);
|
||||||
|
c, e = R(c, d, e, a, b, F2, K2, 6, 6, x);
|
||||||
|
b, d = R(b, c, d, e, a, F2, K2, 5, 13, x);
|
||||||
|
a, c = R(a, b, c, d, e, F2, K2, 12, 11, x);
|
||||||
|
e, b = R(e, a, b, c, d, F2, K2, 7, 5, x);
|
||||||
|
d, a = R(d, e, a, b, c, F2, K2, 5, 12, x); #/* #47 */
|
||||||
|
#/* Round 4 */
|
||||||
|
c, e = R(c, d, e, a, b, F3, K3, 11, 1, x);
|
||||||
|
b, d = R(b, c, d, e, a, F3, K3, 12, 9, x);
|
||||||
|
a, c = R(a, b, c, d, e, F3, K3, 14, 11, x);
|
||||||
|
e, b = R(e, a, b, c, d, F3, K3, 15, 10, x);
|
||||||
|
d, a = R(d, e, a, b, c, F3, K3, 14, 0, x);
|
||||||
|
c, e = R(c, d, e, a, b, F3, K3, 15, 8, x);
|
||||||
|
b, d = R(b, c, d, e, a, F3, K3, 9, 12, x);
|
||||||
|
a, c = R(a, b, c, d, e, F3, K3, 8, 4, x);
|
||||||
|
e, b = R(e, a, b, c, d, F3, K3, 9, 13, x);
|
||||||
|
d, a = R(d, e, a, b, c, F3, K3, 14, 3, x);
|
||||||
|
c, e = R(c, d, e, a, b, F3, K3, 5, 7, x);
|
||||||
|
b, d = R(b, c, d, e, a, F3, K3, 6, 15, x);
|
||||||
|
a, c = R(a, b, c, d, e, F3, K3, 8, 14, x);
|
||||||
|
e, b = R(e, a, b, c, d, F3, K3, 6, 5, x);
|
||||||
|
d, a = R(d, e, a, b, c, F3, K3, 5, 6, x);
|
||||||
|
c, e = R(c, d, e, a, b, F3, K3, 12, 2, x); #/* #63 */
|
||||||
|
#/* Round 5 */
|
||||||
|
b, d = R(b, c, d, e, a, F4, K4, 9, 4, x);
|
||||||
|
a, c = R(a, b, c, d, e, F4, K4, 15, 0, x);
|
||||||
|
e, b = R(e, a, b, c, d, F4, K4, 5, 5, x);
|
||||||
|
d, a = R(d, e, a, b, c, F4, K4, 11, 9, x);
|
||||||
|
c, e = R(c, d, e, a, b, F4, K4, 6, 7, x);
|
||||||
|
b, d = R(b, c, d, e, a, F4, K4, 8, 12, x);
|
||||||
|
a, c = R(a, b, c, d, e, F4, K4, 13, 2, x);
|
||||||
|
e, b = R(e, a, b, c, d, F4, K4, 12, 10, x);
|
||||||
|
d, a = R(d, e, a, b, c, F4, K4, 5, 14, x);
|
||||||
|
c, e = R(c, d, e, a, b, F4, K4, 12, 1, x);
|
||||||
|
b, d = R(b, c, d, e, a, F4, K4, 13, 3, x);
|
||||||
|
a, c = R(a, b, c, d, e, F4, K4, 14, 8, x);
|
||||||
|
e, b = R(e, a, b, c, d, F4, K4, 11, 11, x);
|
||||||
|
d, a = R(d, e, a, b, c, F4, K4, 8, 6, x);
|
||||||
|
c, e = R(c, d, e, a, b, F4, K4, 5, 15, x);
|
||||||
|
b, d = R(b, c, d, e, a, F4, K4, 6, 13, x); #/* #79 */
|
||||||
|
|
||||||
|
aa = a;
|
||||||
|
bb = b;
|
||||||
|
cc = c;
|
||||||
|
dd = d;
|
||||||
|
ee = e;
|
||||||
|
|
||||||
|
a = state[0]
|
||||||
|
b = state[1]
|
||||||
|
c = state[2]
|
||||||
|
d = state[3]
|
||||||
|
e = state[4]
|
||||||
|
|
||||||
|
#/* Parallel round 1 */
|
||||||
|
a, c = R(a, b, c, d, e, F4, KK0, 8, 5, x)
|
||||||
|
e, b = R(e, a, b, c, d, F4, KK0, 9, 14, x)
|
||||||
|
d, a = R(d, e, a, b, c, F4, KK0, 9, 7, x)
|
||||||
|
c, e = R(c, d, e, a, b, F4, KK0, 11, 0, x)
|
||||||
|
b, d = R(b, c, d, e, a, F4, KK0, 13, 9, x)
|
||||||
|
a, c = R(a, b, c, d, e, F4, KK0, 15, 2, x)
|
||||||
|
e, b = R(e, a, b, c, d, F4, KK0, 15, 11, x)
|
||||||
|
d, a = R(d, e, a, b, c, F4, KK0, 5, 4, x)
|
||||||
|
c, e = R(c, d, e, a, b, F4, KK0, 7, 13, x)
|
||||||
|
b, d = R(b, c, d, e, a, F4, KK0, 7, 6, x)
|
||||||
|
a, c = R(a, b, c, d, e, F4, KK0, 8, 15, x)
|
||||||
|
e, b = R(e, a, b, c, d, F4, KK0, 11, 8, x)
|
||||||
|
d, a = R(d, e, a, b, c, F4, KK0, 14, 1, x)
|
||||||
|
c, e = R(c, d, e, a, b, F4, KK0, 14, 10, x)
|
||||||
|
b, d = R(b, c, d, e, a, F4, KK0, 12, 3, x)
|
||||||
|
a, c = R(a, b, c, d, e, F4, KK0, 6, 12, x) #/* #15 */
|
||||||
|
#/* Parallel round 2 */
|
||||||
|
e, b = R(e, a, b, c, d, F3, KK1, 9, 6, x)
|
||||||
|
d, a = R(d, e, a, b, c, F3, KK1, 13, 11, x)
|
||||||
|
c, e = R(c, d, e, a, b, F3, KK1, 15, 3, x)
|
||||||
|
b, d = R(b, c, d, e, a, F3, KK1, 7, 7, x)
|
||||||
|
a, c = R(a, b, c, d, e, F3, KK1, 12, 0, x)
|
||||||
|
e, b = R(e, a, b, c, d, F3, KK1, 8, 13, x)
|
||||||
|
d, a = R(d, e, a, b, c, F3, KK1, 9, 5, x)
|
||||||
|
c, e = R(c, d, e, a, b, F3, KK1, 11, 10, x)
|
||||||
|
b, d = R(b, c, d, e, a, F3, KK1, 7, 14, x)
|
||||||
|
a, c = R(a, b, c, d, e, F3, KK1, 7, 15, x)
|
||||||
|
e, b = R(e, a, b, c, d, F3, KK1, 12, 8, x)
|
||||||
|
d, a = R(d, e, a, b, c, F3, KK1, 7, 12, x)
|
||||||
|
c, e = R(c, d, e, a, b, F3, KK1, 6, 4, x)
|
||||||
|
b, d = R(b, c, d, e, a, F3, KK1, 15, 9, x)
|
||||||
|
a, c = R(a, b, c, d, e, F3, KK1, 13, 1, x)
|
||||||
|
e, b = R(e, a, b, c, d, F3, KK1, 11, 2, x) #/* #31 */
|
||||||
|
#/* Parallel round 3 */
|
||||||
|
d, a = R(d, e, a, b, c, F2, KK2, 9, 15, x)
|
||||||
|
c, e = R(c, d, e, a, b, F2, KK2, 7, 5, x)
|
||||||
|
b, d = R(b, c, d, e, a, F2, KK2, 15, 1, x)
|
||||||
|
a, c = R(a, b, c, d, e, F2, KK2, 11, 3, x)
|
||||||
|
e, b = R(e, a, b, c, d, F2, KK2, 8, 7, x)
|
||||||
|
d, a = R(d, e, a, b, c, F2, KK2, 6, 14, x)
|
||||||
|
c, e = R(c, d, e, a, b, F2, KK2, 6, 6, x)
|
||||||
|
b, d = R(b, c, d, e, a, F2, KK2, 14, 9, x)
|
||||||
|
a, c = R(a, b, c, d, e, F2, KK2, 12, 11, x)
|
||||||
|
e, b = R(e, a, b, c, d, F2, KK2, 13, 8, x)
|
||||||
|
d, a = R(d, e, a, b, c, F2, KK2, 5, 12, x)
|
||||||
|
c, e = R(c, d, e, a, b, F2, KK2, 14, 2, x)
|
||||||
|
b, d = R(b, c, d, e, a, F2, KK2, 13, 10, x)
|
||||||
|
a, c = R(a, b, c, d, e, F2, KK2, 13, 0, x)
|
||||||
|
e, b = R(e, a, b, c, d, F2, KK2, 7, 4, x)
|
||||||
|
d, a = R(d, e, a, b, c, F2, KK2, 5, 13, x) #/* #47 */
|
||||||
|
#/* Parallel round 4 */
|
||||||
|
c, e = R(c, d, e, a, b, F1, KK3, 15, 8, x)
|
||||||
|
b, d = R(b, c, d, e, a, F1, KK3, 5, 6, x)
|
||||||
|
a, c = R(a, b, c, d, e, F1, KK3, 8, 4, x)
|
||||||
|
e, b = R(e, a, b, c, d, F1, KK3, 11, 1, x)
|
||||||
|
d, a = R(d, e, a, b, c, F1, KK3, 14, 3, x)
|
||||||
|
c, e = R(c, d, e, a, b, F1, KK3, 14, 11, x)
|
||||||
|
b, d = R(b, c, d, e, a, F1, KK3, 6, 15, x)
|
||||||
|
a, c = R(a, b, c, d, e, F1, KK3, 14, 0, x)
|
||||||
|
e, b = R(e, a, b, c, d, F1, KK3, 6, 5, x)
|
||||||
|
d, a = R(d, e, a, b, c, F1, KK3, 9, 12, x)
|
||||||
|
c, e = R(c, d, e, a, b, F1, KK3, 12, 2, x)
|
||||||
|
b, d = R(b, c, d, e, a, F1, KK3, 9, 13, x)
|
||||||
|
a, c = R(a, b, c, d, e, F1, KK3, 12, 9, x)
|
||||||
|
e, b = R(e, a, b, c, d, F1, KK3, 5, 7, x)
|
||||||
|
d, a = R(d, e, a, b, c, F1, KK3, 15, 10, x)
|
||||||
|
c, e = R(c, d, e, a, b, F1, KK3, 8, 14, x) #/* #63 */
|
||||||
|
#/* Parallel round 5 */
|
||||||
|
b, d = R(b, c, d, e, a, F0, KK4, 8, 12, x)
|
||||||
|
a, c = R(a, b, c, d, e, F0, KK4, 5, 15, x)
|
||||||
|
e, b = R(e, a, b, c, d, F0, KK4, 12, 10, x)
|
||||||
|
d, a = R(d, e, a, b, c, F0, KK4, 9, 4, x)
|
||||||
|
c, e = R(c, d, e, a, b, F0, KK4, 12, 1, x)
|
||||||
|
b, d = R(b, c, d, e, a, F0, KK4, 5, 5, x)
|
||||||
|
a, c = R(a, b, c, d, e, F0, KK4, 14, 8, x)
|
||||||
|
e, b = R(e, a, b, c, d, F0, KK4, 6, 7, x)
|
||||||
|
d, a = R(d, e, a, b, c, F0, KK4, 8, 6, x)
|
||||||
|
c, e = R(c, d, e, a, b, F0, KK4, 13, 2, x)
|
||||||
|
b, d = R(b, c, d, e, a, F0, KK4, 6, 13, x)
|
||||||
|
a, c = R(a, b, c, d, e, F0, KK4, 5, 14, x)
|
||||||
|
e, b = R(e, a, b, c, d, F0, KK4, 15, 0, x)
|
||||||
|
d, a = R(d, e, a, b, c, F0, KK4, 13, 3, x)
|
||||||
|
c, e = R(c, d, e, a, b, F0, KK4, 11, 9, x)
|
||||||
|
b, d = R(b, c, d, e, a, F0, KK4, 11, 11, x) #/* #79 */
|
||||||
|
|
||||||
|
t = (state[1] + cc + d) % 0x100000000;
|
||||||
|
state[1] = (state[2] + dd + e) % 0x100000000;
|
||||||
|
state[2] = (state[3] + ee + a) % 0x100000000;
|
||||||
|
state[3] = (state[4] + aa + b) % 0x100000000;
|
||||||
|
state[4] = (state[0] + bb + c) % 0x100000000;
|
||||||
|
state[0] = t % 0x100000000;
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def RMD160Update(ctx, inp, inplen):
|
||||||
|
if type(inp) == str:
|
||||||
|
inp = [ord(i)&0xff for i in inp]
|
||||||
|
|
||||||
|
have = int((ctx.count // 8) % 64)
|
||||||
|
inplen = int(inplen)
|
||||||
|
need = 64 - have
|
||||||
|
ctx.count += 8 * inplen
|
||||||
|
off = 0
|
||||||
|
if inplen >= need:
|
||||||
|
if have:
|
||||||
|
for i in range(need):
|
||||||
|
ctx.buffer[have+i] = inp[i]
|
||||||
|
RMD160Transform(ctx.state, ctx.buffer)
|
||||||
|
off = need
|
||||||
|
have = 0
|
||||||
|
while off + 64 <= inplen:
|
||||||
|
RMD160Transform(ctx.state, inp[off:]) #<---
|
||||||
|
off += 64
|
||||||
|
if off < inplen:
|
||||||
|
# memcpy(ctx->buffer + have, input+off, len-off);
|
||||||
|
for i in range(inplen - off):
|
||||||
|
ctx.buffer[have+i] = inp[off+i]
|
||||||
|
|
||||||
|
def RMD160Final(ctx):
|
||||||
|
size = struct.pack("<Q", ctx.count)
|
||||||
|
padlen = 64 - ((ctx.count // 8) % 64)
|
||||||
|
if padlen < 1+8:
|
||||||
|
padlen += 64
|
||||||
|
RMD160Update(ctx, PADDING, padlen-8)
|
||||||
|
RMD160Update(ctx, size, 8)
|
||||||
|
return struct.pack("<5L", *ctx.state)
|
||||||
|
|
||||||
|
|
||||||
|
assert '37f332f68db77bd9d7edd4969571ad671cf9dd3b' == \
|
||||||
|
new('The quick brown fox jumps over the lazy dog').hexdigest()
|
||||||
|
assert '132072df690933835eb8b6ad0b77e7b6f14acad7' == \
|
||||||
|
new('The quick brown fox jumps over the lazy cog').hexdigest()
|
||||||
|
assert '9c1185a5c5e9fc54612808977ee8f548b2258d31' == \
|
||||||
|
new('').hexdigest()
|
100
src/lib/pybitcointools/stealth.py
Normal file
100
src/lib/pybitcointools/stealth.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
from . import main as main
|
||||||
|
from . import transaction as tx
|
||||||
|
|
||||||
|
# Shared secrets and uncovering pay keys
|
||||||
|
|
||||||
|
|
||||||
|
def shared_secret_sender(scan_pubkey, ephem_privkey):
|
||||||
|
shared_point = main.multiply(scan_pubkey, ephem_privkey)
|
||||||
|
shared_secret = main.sha256(main.encode_pubkey(shared_point, 'bin_compressed'))
|
||||||
|
return shared_secret
|
||||||
|
|
||||||
|
|
||||||
|
def shared_secret_receiver(ephem_pubkey, scan_privkey):
|
||||||
|
shared_point = main.multiply(ephem_pubkey, scan_privkey)
|
||||||
|
shared_secret = main.sha256(main.encode_pubkey(shared_point, 'bin_compressed'))
|
||||||
|
return shared_secret
|
||||||
|
|
||||||
|
|
||||||
|
def uncover_pay_pubkey_sender(scan_pubkey, spend_pubkey, ephem_privkey):
|
||||||
|
shared_secret = shared_secret_sender(scan_pubkey, ephem_privkey)
|
||||||
|
return main.add_pubkeys(spend_pubkey, main.privtopub(shared_secret))
|
||||||
|
|
||||||
|
|
||||||
|
def uncover_pay_pubkey_receiver(scan_privkey, spend_pubkey, ephem_pubkey):
|
||||||
|
shared_secret = shared_secret_receiver(ephem_pubkey, scan_privkey)
|
||||||
|
return main.add_pubkeys(spend_pubkey, main.privtopub(shared_secret))
|
||||||
|
|
||||||
|
|
||||||
|
def uncover_pay_privkey(scan_privkey, spend_privkey, ephem_pubkey):
|
||||||
|
shared_secret = shared_secret_receiver(ephem_pubkey, scan_privkey)
|
||||||
|
return main.add_privkeys(spend_privkey, shared_secret)
|
||||||
|
|
||||||
|
# Address encoding
|
||||||
|
|
||||||
|
# Functions for basic stealth addresses,
|
||||||
|
# i.e. one scan key, one spend key, no prefix
|
||||||
|
|
||||||
|
|
||||||
|
def pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, magic_byte=42):
|
||||||
|
# magic_byte = 42 for mainnet, 43 for testnet.
|
||||||
|
hex_scankey = main.encode_pubkey(scan_pubkey, 'hex_compressed')
|
||||||
|
hex_spendkey = main.encode_pubkey(spend_pubkey, 'hex_compressed')
|
||||||
|
hex_data = '00{0:066x}01{1:066x}0100'.format(int(hex_scankey, 16), int(hex_spendkey, 16))
|
||||||
|
addr = main.hex_to_b58check(hex_data, magic_byte)
|
||||||
|
return addr
|
||||||
|
|
||||||
|
|
||||||
|
def basic_stealth_address_to_pubkeys(stealth_address):
|
||||||
|
hex_data = main.b58check_to_hex(stealth_address)
|
||||||
|
if len(hex_data) != 140:
|
||||||
|
raise Exception('Stealth address is not of basic type (one scan key, one spend key, no prefix)')
|
||||||
|
|
||||||
|
scan_pubkey = hex_data[2:68]
|
||||||
|
spend_pubkey = hex_data[70:136]
|
||||||
|
return scan_pubkey, spend_pubkey
|
||||||
|
|
||||||
|
# Sending stealth payments
|
||||||
|
|
||||||
|
|
||||||
|
def mk_stealth_metadata_script(ephem_pubkey, nonce):
|
||||||
|
op_return = '6a'
|
||||||
|
msg_size = '26'
|
||||||
|
version = '06'
|
||||||
|
return op_return + msg_size + version + '{0:08x}'.format(nonce) + main.encode_pubkey(ephem_pubkey, 'hex_compressed')
|
||||||
|
|
||||||
|
|
||||||
|
def mk_stealth_tx_outputs(stealth_addr, value, ephem_privkey, nonce, network='btc'):
|
||||||
|
|
||||||
|
scan_pubkey, spend_pubkey = basic_stealth_address_to_pubkeys(stealth_addr)
|
||||||
|
|
||||||
|
if network == 'btc':
|
||||||
|
btc_magic_byte = 42
|
||||||
|
if stealth_addr != pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, btc_magic_byte):
|
||||||
|
raise Exception('Invalid btc mainnet stealth address: ' + stealth_addr)
|
||||||
|
magic_byte_addr = 0
|
||||||
|
|
||||||
|
elif network == 'testnet':
|
||||||
|
testnet_magic_byte = 43
|
||||||
|
if stealth_addr != pubkeys_to_basic_stealth_address(scan_pubkey, spend_pubkey, testnet_magic_byte):
|
||||||
|
raise Exception('Invalid testnet stealth address: ' + stealth_addr)
|
||||||
|
magic_byte_addr = 111
|
||||||
|
|
||||||
|
ephem_pubkey = main.privkey_to_pubkey(ephem_privkey)
|
||||||
|
output0 = {'script': mk_stealth_metadata_script(ephem_pubkey, nonce),
|
||||||
|
'value': 0}
|
||||||
|
|
||||||
|
pay_pubkey = uncover_pay_pubkey_sender(scan_pubkey, spend_pubkey, ephem_privkey)
|
||||||
|
pay_addr = main.pubkey_to_address(pay_pubkey, magic_byte_addr)
|
||||||
|
output1 = {'address': pay_addr,
|
||||||
|
'value': value}
|
||||||
|
|
||||||
|
return [output0, output1]
|
||||||
|
|
||||||
|
# Receiving stealth payments
|
||||||
|
|
||||||
|
|
||||||
|
def ephem_pubkey_from_tx_script(stealth_tx_script):
|
||||||
|
if len(stealth_tx_script) != 80:
|
||||||
|
raise Exception('Wrong format for stealth tx output')
|
||||||
|
return stealth_tx_script[14:]
|
514
src/lib/pybitcointools/transaction.py
Normal file
514
src/lib/pybitcointools/transaction.py
Normal file
|
@ -0,0 +1,514 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
import binascii, re, json, copy, sys
|
||||||
|
from .main import *
|
||||||
|
from _functools import reduce
|
||||||
|
|
||||||
|
### Hex to bin converter and vice versa for objects
|
||||||
|
|
||||||
|
|
||||||
|
def json_is_base(obj, base):
|
||||||
|
if not is_python2 and isinstance(obj, bytes):
|
||||||
|
return False
|
||||||
|
|
||||||
|
alpha = get_code_string(base)
|
||||||
|
if isinstance(obj, string_types):
|
||||||
|
for i in range(len(obj)):
|
||||||
|
if alpha.find(obj[i]) == -1:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
elif isinstance(obj, int_types) or obj is None:
|
||||||
|
return True
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
for i in range(len(obj)):
|
||||||
|
if not json_is_base(obj[i], base):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
for x in obj:
|
||||||
|
if not json_is_base(obj[x], base):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def json_changebase(obj, changer):
|
||||||
|
if isinstance(obj, string_or_bytes_types):
|
||||||
|
return changer(obj)
|
||||||
|
elif isinstance(obj, int_types) or obj is None:
|
||||||
|
return obj
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
return [json_changebase(x, changer) for x in obj]
|
||||||
|
return dict((x, json_changebase(obj[x], changer)) for x in obj)
|
||||||
|
|
||||||
|
# Transaction serialization and deserialization
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize(tx):
|
||||||
|
if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
#tx = bytes(bytearray.fromhex(tx))
|
||||||
|
return json_changebase(deserialize(binascii.unhexlify(tx)),
|
||||||
|
lambda x: safe_hexlify(x))
|
||||||
|
# http://stackoverflow.com/questions/4851463/python-closure-write-to-variable-in-parent-scope
|
||||||
|
# Python's scoping rules are demented, requiring me to make pos an object
|
||||||
|
# so that it is call-by-reference
|
||||||
|
pos = [0]
|
||||||
|
|
||||||
|
def read_as_int(bytez):
|
||||||
|
pos[0] += bytez
|
||||||
|
return decode(tx[pos[0]-bytez:pos[0]][::-1], 256)
|
||||||
|
|
||||||
|
def read_var_int():
|
||||||
|
pos[0] += 1
|
||||||
|
|
||||||
|
val = from_byte_to_int(tx[pos[0]-1])
|
||||||
|
if val < 253:
|
||||||
|
return val
|
||||||
|
return read_as_int(pow(2, val - 252))
|
||||||
|
|
||||||
|
def read_bytes(bytez):
|
||||||
|
pos[0] += bytez
|
||||||
|
return tx[pos[0]-bytez:pos[0]]
|
||||||
|
|
||||||
|
def read_var_string():
|
||||||
|
size = read_var_int()
|
||||||
|
return read_bytes(size)
|
||||||
|
|
||||||
|
obj = {"ins": [], "outs": []}
|
||||||
|
obj["version"] = read_as_int(4)
|
||||||
|
ins = read_var_int()
|
||||||
|
for i in range(ins):
|
||||||
|
obj["ins"].append({
|
||||||
|
"outpoint": {
|
||||||
|
"hash": read_bytes(32)[::-1],
|
||||||
|
"index": read_as_int(4)
|
||||||
|
},
|
||||||
|
"script": read_var_string(),
|
||||||
|
"sequence": read_as_int(4)
|
||||||
|
})
|
||||||
|
outs = read_var_int()
|
||||||
|
for i in range(outs):
|
||||||
|
obj["outs"].append({
|
||||||
|
"value": read_as_int(8),
|
||||||
|
"script": read_var_string()
|
||||||
|
})
|
||||||
|
obj["locktime"] = read_as_int(4)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def serialize(txobj):
|
||||||
|
#if isinstance(txobj, bytes):
|
||||||
|
# txobj = bytes_to_hex_string(txobj)
|
||||||
|
o = []
|
||||||
|
if json_is_base(txobj, 16):
|
||||||
|
json_changedbase = json_changebase(txobj, lambda x: binascii.unhexlify(x))
|
||||||
|
hexlified = safe_hexlify(serialize(json_changedbase))
|
||||||
|
return hexlified
|
||||||
|
o.append(encode(txobj["version"], 256, 4)[::-1])
|
||||||
|
o.append(num_to_var_int(len(txobj["ins"])))
|
||||||
|
for inp in txobj["ins"]:
|
||||||
|
o.append(inp["outpoint"]["hash"][::-1])
|
||||||
|
o.append(encode(inp["outpoint"]["index"], 256, 4)[::-1])
|
||||||
|
o.append(num_to_var_int(len(inp["script"]))+(inp["script"] if inp["script"] or is_python2 else bytes()))
|
||||||
|
o.append(encode(inp["sequence"], 256, 4)[::-1])
|
||||||
|
o.append(num_to_var_int(len(txobj["outs"])))
|
||||||
|
for out in txobj["outs"]:
|
||||||
|
o.append(encode(out["value"], 256, 8)[::-1])
|
||||||
|
o.append(num_to_var_int(len(out["script"]))+out["script"])
|
||||||
|
o.append(encode(txobj["locktime"], 256, 4)[::-1])
|
||||||
|
|
||||||
|
return ''.join(o) if is_python2 else reduce(lambda x,y: x+y, o, bytes())
|
||||||
|
|
||||||
|
# Hashing transactions for signing
|
||||||
|
|
||||||
|
SIGHASH_ALL = 1
|
||||||
|
SIGHASH_NONE = 2
|
||||||
|
SIGHASH_SINGLE = 3
|
||||||
|
# this works like SIGHASH_ANYONECANPAY | SIGHASH_ALL, might as well make it explicit while
|
||||||
|
# we fix the constant
|
||||||
|
SIGHASH_ANYONECANPAY = 0x81
|
||||||
|
|
||||||
|
|
||||||
|
def signature_form(tx, i, script, hashcode=SIGHASH_ALL):
|
||||||
|
i, hashcode = int(i), int(hashcode)
|
||||||
|
if isinstance(tx, string_or_bytes_types):
|
||||||
|
return serialize(signature_form(deserialize(tx), i, script, hashcode))
|
||||||
|
newtx = copy.deepcopy(tx)
|
||||||
|
for inp in newtx["ins"]:
|
||||||
|
inp["script"] = ""
|
||||||
|
newtx["ins"][i]["script"] = script
|
||||||
|
if hashcode == SIGHASH_NONE:
|
||||||
|
newtx["outs"] = []
|
||||||
|
elif hashcode == SIGHASH_SINGLE:
|
||||||
|
newtx["outs"] = newtx["outs"][:len(newtx["ins"])]
|
||||||
|
for out in newtx["outs"][:len(newtx["ins"]) - 1]:
|
||||||
|
out['value'] = 2**64 - 1
|
||||||
|
out['script'] = ""
|
||||||
|
elif hashcode == SIGHASH_ANYONECANPAY:
|
||||||
|
newtx["ins"] = [newtx["ins"][i]]
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return newtx
|
||||||
|
|
||||||
|
# Making the actual signatures
|
||||||
|
|
||||||
|
|
||||||
|
def der_encode_sig(v, r, s):
|
||||||
|
b1, b2 = safe_hexlify(encode(r, 256)), safe_hexlify(encode(s, 256))
|
||||||
|
if len(b1) and b1[0] in '89abcdef':
|
||||||
|
b1 = '00' + b1
|
||||||
|
if len(b2) and b2[0] in '89abcdef':
|
||||||
|
b2 = '00' + b2
|
||||||
|
left = '02'+encode(len(b1)//2, 16, 2)+b1
|
||||||
|
right = '02'+encode(len(b2)//2, 16, 2)+b2
|
||||||
|
return '30'+encode(len(left+right)//2, 16, 2)+left+right
|
||||||
|
|
||||||
|
def der_decode_sig(sig):
|
||||||
|
leftlen = decode(sig[6:8], 16)*2
|
||||||
|
left = sig[8:8+leftlen]
|
||||||
|
rightlen = decode(sig[10+leftlen:12+leftlen], 16)*2
|
||||||
|
right = sig[12+leftlen:12+leftlen+rightlen]
|
||||||
|
return (None, decode(left, 16), decode(right, 16))
|
||||||
|
|
||||||
|
def is_bip66(sig):
|
||||||
|
"""Checks hex DER sig for BIP66 consistency"""
|
||||||
|
#https://raw.githubusercontent.com/bitcoin/bips/master/bip-0066.mediawiki
|
||||||
|
#0x30 [total-len] 0x02 [R-len] [R] 0x02 [S-len] [S] [sighash]
|
||||||
|
sig = bytearray.fromhex(sig) if re.match('^[0-9a-fA-F]*$', sig) else bytearray(sig)
|
||||||
|
if (sig[0] == 0x30) and (sig[1] == len(sig)-2): # check if sighash is missing
|
||||||
|
sig.extend(b"\1") # add SIGHASH_ALL for testing
|
||||||
|
#assert (sig[-1] & 124 == 0) and (not not sig[-1]), "Bad SIGHASH value"
|
||||||
|
|
||||||
|
if len(sig) < 9 or len(sig) > 73: return False
|
||||||
|
if (sig[0] != 0x30): return False
|
||||||
|
if (sig[1] != len(sig)-3): return False
|
||||||
|
rlen = sig[3]
|
||||||
|
if (5+rlen >= len(sig)): return False
|
||||||
|
slen = sig[5+rlen]
|
||||||
|
if (rlen + slen + 7 != len(sig)): return False
|
||||||
|
if (sig[2] != 0x02): return False
|
||||||
|
if (rlen == 0): return False
|
||||||
|
if (sig[4] & 0x80): return False
|
||||||
|
if (rlen > 1 and (sig[4] == 0x00) and not (sig[5] & 0x80)): return False
|
||||||
|
if (sig[4+rlen] != 0x02): return False
|
||||||
|
if (slen == 0): return False
|
||||||
|
if (sig[rlen+6] & 0x80): return False
|
||||||
|
if (slen > 1 and (sig[6+rlen] == 0x00) and not (sig[7+rlen] & 0x80)):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def txhash(tx, hashcode=None):
|
||||||
|
if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = changebase(tx, 16, 256)
|
||||||
|
if hashcode:
|
||||||
|
return dbl_sha256(from_string_to_bytes(tx) + encode(int(hashcode), 256, 4)[::-1])
|
||||||
|
else:
|
||||||
|
return safe_hexlify(bin_dbl_sha256(tx)[::-1])
|
||||||
|
|
||||||
|
|
||||||
|
def bin_txhash(tx, hashcode=None):
|
||||||
|
return binascii.unhexlify(txhash(tx, hashcode))
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_tx_sign(tx, priv, hashcode=SIGHASH_ALL):
|
||||||
|
rawsig = ecdsa_raw_sign(bin_txhash(tx, hashcode), priv)
|
||||||
|
return der_encode_sig(*rawsig)+encode(hashcode, 16, 2)
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_tx_verify(tx, sig, pub, hashcode=SIGHASH_ALL):
|
||||||
|
return ecdsa_raw_verify(bin_txhash(tx, hashcode), der_decode_sig(sig), pub)
|
||||||
|
|
||||||
|
|
||||||
|
def ecdsa_tx_recover(tx, sig, hashcode=SIGHASH_ALL):
|
||||||
|
z = bin_txhash(tx, hashcode)
|
||||||
|
_, r, s = der_decode_sig(sig)
|
||||||
|
left = ecdsa_raw_recover(z, (0, r, s))
|
||||||
|
right = ecdsa_raw_recover(z, (1, r, s))
|
||||||
|
return (encode_pubkey(left, 'hex'), encode_pubkey(right, 'hex'))
|
||||||
|
|
||||||
|
# Scripts
|
||||||
|
|
||||||
|
|
||||||
|
def mk_pubkey_script(addr):
|
||||||
|
# Keep the auxiliary functions around for altcoins' sake
|
||||||
|
return '76a914' + b58check_to_hex(addr) + '88ac'
|
||||||
|
|
||||||
|
|
||||||
|
def mk_scripthash_script(addr):
|
||||||
|
return 'a914' + b58check_to_hex(addr) + '87'
|
||||||
|
|
||||||
|
# Address representation to output script
|
||||||
|
|
||||||
|
|
||||||
|
def address_to_script(addr):
|
||||||
|
if addr[0] == '3' or addr[0] == '2':
|
||||||
|
return mk_scripthash_script(addr)
|
||||||
|
else:
|
||||||
|
return mk_pubkey_script(addr)
|
||||||
|
|
||||||
|
# Output script to address representation
|
||||||
|
|
||||||
|
|
||||||
|
def script_to_address(script, vbyte=0):
|
||||||
|
if re.match('^[0-9a-fA-F]*$', script):
|
||||||
|
script = binascii.unhexlify(script)
|
||||||
|
if script[:3] == b'\x76\xa9\x14' and script[-2:] == b'\x88\xac' and len(script) == 25:
|
||||||
|
return bin_to_b58check(script[3:-2], vbyte) # pubkey hash addresses
|
||||||
|
else:
|
||||||
|
if vbyte in [111, 196]:
|
||||||
|
# Testnet
|
||||||
|
scripthash_byte = 196
|
||||||
|
elif vbyte == 0:
|
||||||
|
# Mainnet
|
||||||
|
scripthash_byte = 5
|
||||||
|
else:
|
||||||
|
scripthash_byte = vbyte
|
||||||
|
# BIP0016 scripthash addresses
|
||||||
|
return bin_to_b58check(script[2:-1], scripthash_byte)
|
||||||
|
|
||||||
|
|
||||||
|
def p2sh_scriptaddr(script, magicbyte=5):
|
||||||
|
if re.match('^[0-9a-fA-F]*$', script):
|
||||||
|
script = binascii.unhexlify(script)
|
||||||
|
return hex_to_b58check(hash160(script), magicbyte)
|
||||||
|
scriptaddr = p2sh_scriptaddr
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_script(script):
|
||||||
|
if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
|
||||||
|
return json_changebase(deserialize_script(binascii.unhexlify(script)),
|
||||||
|
lambda x: safe_hexlify(x))
|
||||||
|
out, pos = [], 0
|
||||||
|
while pos < len(script):
|
||||||
|
code = from_byte_to_int(script[pos])
|
||||||
|
if code == 0:
|
||||||
|
out.append(None)
|
||||||
|
pos += 1
|
||||||
|
elif code <= 75:
|
||||||
|
out.append(script[pos+1:pos+1+code])
|
||||||
|
pos += 1 + code
|
||||||
|
elif code <= 78:
|
||||||
|
szsz = pow(2, code - 76)
|
||||||
|
sz = decode(script[pos+szsz: pos:-1], 256)
|
||||||
|
out.append(script[pos + 1 + szsz:pos + 1 + szsz + sz])
|
||||||
|
pos += 1 + szsz + sz
|
||||||
|
elif code <= 96:
|
||||||
|
out.append(code - 80)
|
||||||
|
pos += 1
|
||||||
|
else:
|
||||||
|
out.append(code)
|
||||||
|
pos += 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_script_unit(unit):
|
||||||
|
if isinstance(unit, int):
|
||||||
|
if unit < 16:
|
||||||
|
return from_int_to_byte(unit + 80)
|
||||||
|
else:
|
||||||
|
return from_int_to_byte(unit)
|
||||||
|
elif unit is None:
|
||||||
|
return b'\x00'
|
||||||
|
else:
|
||||||
|
if len(unit) <= 75:
|
||||||
|
return from_int_to_byte(len(unit))+unit
|
||||||
|
elif len(unit) < 256:
|
||||||
|
return from_int_to_byte(76)+from_int_to_byte(len(unit))+unit
|
||||||
|
elif len(unit) < 65536:
|
||||||
|
return from_int_to_byte(77)+encode(len(unit), 256, 2)[::-1]+unit
|
||||||
|
else:
|
||||||
|
return from_int_to_byte(78)+encode(len(unit), 256, 4)[::-1]+unit
|
||||||
|
|
||||||
|
|
||||||
|
if is_python2:
|
||||||
|
def serialize_script(script):
|
||||||
|
if json_is_base(script, 16):
|
||||||
|
return binascii.hexlify(serialize_script(json_changebase(script,
|
||||||
|
lambda x: binascii.unhexlify(x))))
|
||||||
|
return ''.join(map(serialize_script_unit, script))
|
||||||
|
else:
|
||||||
|
def serialize_script(script):
|
||||||
|
if json_is_base(script, 16):
|
||||||
|
return safe_hexlify(serialize_script(json_changebase(script,
|
||||||
|
lambda x: binascii.unhexlify(x))))
|
||||||
|
|
||||||
|
result = bytes()
|
||||||
|
for b in map(serialize_script_unit, script):
|
||||||
|
result += b if isinstance(b, bytes) else bytes(b, 'utf-8')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def mk_multisig_script(*args): # [pubs],k or pub1,pub2...pub[n],k
|
||||||
|
if isinstance(args[0], list):
|
||||||
|
pubs, k = args[0], int(args[1])
|
||||||
|
else:
|
||||||
|
pubs = list(filter(lambda x: len(str(x)) >= 32, args))
|
||||||
|
k = int(args[len(pubs)])
|
||||||
|
return serialize_script([k]+pubs+[len(pubs)]+[0xae])
|
||||||
|
|
||||||
|
# Signing and verifying
|
||||||
|
|
||||||
|
|
||||||
|
def verify_tx_input(tx, i, script, sig, pub):
|
||||||
|
if re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = binascii.unhexlify(tx)
|
||||||
|
if re.match('^[0-9a-fA-F]*$', script):
|
||||||
|
script = binascii.unhexlify(script)
|
||||||
|
if not re.match('^[0-9a-fA-F]*$', sig):
|
||||||
|
sig = safe_hexlify(sig)
|
||||||
|
hashcode = decode(sig[-2:], 16)
|
||||||
|
modtx = signature_form(tx, int(i), script, hashcode)
|
||||||
|
return ecdsa_tx_verify(modtx, sig, pub, hashcode)
|
||||||
|
|
||||||
|
|
||||||
|
def sign(tx, i, priv, hashcode=SIGHASH_ALL):
|
||||||
|
i = int(i)
|
||||||
|
if (not is_python2 and isinstance(re, bytes)) or not re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
return binascii.unhexlify(sign(safe_hexlify(tx), i, priv))
|
||||||
|
if len(priv) <= 33:
|
||||||
|
priv = safe_hexlify(priv)
|
||||||
|
pub = privkey_to_pubkey(priv)
|
||||||
|
address = pubkey_to_address(pub)
|
||||||
|
signing_tx = signature_form(tx, i, mk_pubkey_script(address), hashcode)
|
||||||
|
sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
|
||||||
|
txobj = deserialize(tx)
|
||||||
|
txobj["ins"][i]["script"] = serialize_script([sig, pub])
|
||||||
|
return serialize(txobj)
|
||||||
|
|
||||||
|
|
||||||
|
def signall(tx, priv):
|
||||||
|
# if priv is a dictionary, assume format is
|
||||||
|
# { 'txinhash:txinidx' : privkey }
|
||||||
|
if isinstance(priv, dict):
|
||||||
|
for e, i in enumerate(deserialize(tx)["ins"]):
|
||||||
|
k = priv["%s:%d" % (i["outpoint"]["hash"], i["outpoint"]["index"])]
|
||||||
|
tx = sign(tx, e, k)
|
||||||
|
else:
|
||||||
|
for i in range(len(deserialize(tx)["ins"])):
|
||||||
|
tx = sign(tx, i, priv)
|
||||||
|
return tx
|
||||||
|
|
||||||
|
|
||||||
|
def multisign(tx, i, script, pk, hashcode=SIGHASH_ALL):
|
||||||
|
if re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
tx = binascii.unhexlify(tx)
|
||||||
|
if re.match('^[0-9a-fA-F]*$', script):
|
||||||
|
script = binascii.unhexlify(script)
|
||||||
|
modtx = signature_form(tx, i, script, hashcode)
|
||||||
|
return ecdsa_tx_sign(modtx, pk, hashcode)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_multisignatures(*args):
|
||||||
|
# tx,i,script,sigs OR tx,i,script,sig1,sig2...,sig[n]
|
||||||
|
tx, i, script = args[0], int(args[1]), args[2]
|
||||||
|
sigs = args[3] if isinstance(args[3], list) else list(args[3:])
|
||||||
|
|
||||||
|
if isinstance(script, str) and re.match('^[0-9a-fA-F]*$', script):
|
||||||
|
script = binascii.unhexlify(script)
|
||||||
|
sigs = [binascii.unhexlify(x) if x[:2] == '30' else x for x in sigs]
|
||||||
|
if isinstance(tx, str) and re.match('^[0-9a-fA-F]*$', tx):
|
||||||
|
return safe_hexlify(apply_multisignatures(binascii.unhexlify(tx), i, script, sigs))
|
||||||
|
|
||||||
|
# Not pushing empty elements on the top of the stack if passing no
|
||||||
|
# script (in case of bare multisig inputs there is no script)
|
||||||
|
script_blob = [] if script.__len__() == 0 else [script]
|
||||||
|
|
||||||
|
txobj = deserialize(tx)
|
||||||
|
txobj["ins"][i]["script"] = serialize_script([None]+sigs+script_blob)
|
||||||
|
return serialize(txobj)
|
||||||
|
|
||||||
|
|
||||||
|
def is_inp(arg):
|
||||||
|
return len(arg) > 64 or "output" in arg or "outpoint" in arg
|
||||||
|
|
||||||
|
|
||||||
|
def mktx(*args):
|
||||||
|
# [in0, in1...],[out0, out1...] or in0, in1 ... out0 out1 ...
|
||||||
|
ins, outs = [], []
|
||||||
|
for arg in args:
|
||||||
|
if isinstance(arg, list):
|
||||||
|
for a in arg: (ins if is_inp(a) else outs).append(a)
|
||||||
|
else:
|
||||||
|
(ins if is_inp(arg) else outs).append(arg)
|
||||||
|
|
||||||
|
txobj = {"locktime": 0, "version": 1, "ins": [], "outs": []}
|
||||||
|
for i in ins:
|
||||||
|
if isinstance(i, dict) and "outpoint" in i:
|
||||||
|
txobj["ins"].append(i)
|
||||||
|
else:
|
||||||
|
if isinstance(i, dict) and "output" in i:
|
||||||
|
i = i["output"]
|
||||||
|
txobj["ins"].append({
|
||||||
|
"outpoint": {"hash": i[:64], "index": int(i[65:])},
|
||||||
|
"script": "",
|
||||||
|
"sequence": 4294967295
|
||||||
|
})
|
||||||
|
for o in outs:
|
||||||
|
if isinstance(o, string_or_bytes_types):
|
||||||
|
addr = o[:o.find(':')]
|
||||||
|
val = int(o[o.find(':')+1:])
|
||||||
|
o = {}
|
||||||
|
if re.match('^[0-9a-fA-F]*$', addr):
|
||||||
|
o["script"] = addr
|
||||||
|
else:
|
||||||
|
o["address"] = addr
|
||||||
|
o["value"] = val
|
||||||
|
|
||||||
|
outobj = {}
|
||||||
|
if "address" in o:
|
||||||
|
outobj["script"] = address_to_script(o["address"])
|
||||||
|
elif "script" in o:
|
||||||
|
outobj["script"] = o["script"]
|
||||||
|
else:
|
||||||
|
raise Exception("Could not find 'address' or 'script' in output.")
|
||||||
|
outobj["value"] = o["value"]
|
||||||
|
txobj["outs"].append(outobj)
|
||||||
|
|
||||||
|
return serialize(txobj)
|
||||||
|
|
||||||
|
|
||||||
|
def select(unspent, value):
|
||||||
|
value = int(value)
|
||||||
|
high = [u for u in unspent if u["value"] >= value]
|
||||||
|
high.sort(key=lambda u: u["value"])
|
||||||
|
low = [u for u in unspent if u["value"] < value]
|
||||||
|
low.sort(key=lambda u: -u["value"])
|
||||||
|
if len(high):
|
||||||
|
return [high[0]]
|
||||||
|
i, tv = 0, 0
|
||||||
|
while tv < value and i < len(low):
|
||||||
|
tv += low[i]["value"]
|
||||||
|
i += 1
|
||||||
|
if tv < value:
|
||||||
|
raise Exception("Not enough funds")
|
||||||
|
return low[:i]
|
||||||
|
|
||||||
|
# Only takes inputs of the form { "output": blah, "value": foo }
|
||||||
|
|
||||||
|
|
||||||
|
def mksend(*args):
|
||||||
|
argz, change, fee = args[:-2], args[-2], int(args[-1])
|
||||||
|
ins, outs = [], []
|
||||||
|
for arg in argz:
|
||||||
|
if isinstance(arg, list):
|
||||||
|
for a in arg:
|
||||||
|
(ins if is_inp(a) else outs).append(a)
|
||||||
|
else:
|
||||||
|
(ins if is_inp(arg) else outs).append(arg)
|
||||||
|
|
||||||
|
isum = sum([i["value"] for i in ins])
|
||||||
|
osum, outputs2 = 0, []
|
||||||
|
for o in outs:
|
||||||
|
if isinstance(o, string_types):
|
||||||
|
o2 = {
|
||||||
|
"address": o[:o.find(':')],
|
||||||
|
"value": int(o[o.find(':')+1:])
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
o2 = o
|
||||||
|
outputs2.append(o2)
|
||||||
|
osum += o2["value"]
|
||||||
|
|
||||||
|
if isum < osum+fee:
|
||||||
|
raise Exception("Not enough money")
|
||||||
|
elif isum > osum+fee+5430:
|
||||||
|
outputs2 += [{"address": change, "value": isum-osum-fee}]
|
||||||
|
|
||||||
|
return mktx(ins, outputs2)
|
Loading…
Reference in a new issue