Remove included win_inet_pton, websocket, rsa, socks, pyelliptic, pybitcointools, pyasn1, opensslVerify, merkletools, geventwebsocket, BitcoinECC, bencode module
This commit is contained in:
parent
ff5004cb8d
commit
3eae349a0a
142 changed files with 0 additions and 27935 deletions
|
@ -1,27 +0,0 @@
|
|||
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.
|
|
@ -1 +0,0 @@
|
|||
include bitcoin/english.txt
|
|
@ -1,142 +0,0 @@
|
|||
# Pybitcointools, Python library for Bitcoin signatures and transactions
|
||||
|
||||
### Advantages:
|
||||
|
||||
* Functions have a simple interface, inputting and outputting in standard formats
|
||||
* No classes
|
||||
* Many functions can be taken out and used individually
|
||||
* Supports binary, hex and base58
|
||||
* Transaction deserialization format almost compatible with BitcoinJS
|
||||
* Electrum and BIP0032 support
|
||||
* Make and publish a transaction all in a single command line instruction
|
||||
* Includes non-bitcoin-specific conversion and JSON utilities
|
||||
|
||||
### Disadvantages:
|
||||
|
||||
* Not a full node, has no idea what blocks are
|
||||
* Relies on centralized service (blockchain.info) for blockchain operations, although operations do have backups (eligius, blockr.io)
|
||||
|
||||
### Example usage (best way to learn :) ):
|
||||
|
||||
> from bitcoin import *
|
||||
> priv = sha256('some big long brainwallet password')
|
||||
> priv
|
||||
'57c617d9b4e1f7af6ec97ca2ff57e94a28279a7eedd4d12a99fa11170e94f5a4'
|
||||
> pub = privtopub(priv)
|
||||
> pub
|
||||
'0420f34c2786b4bae593e22596631b025f3ff46e200fc1d4b52ef49bbdc2ed00b26c584b7e32523fb01be2294a1f8a5eb0cf71a203cc034ced46ea92a8df16c6e9'
|
||||
> addr = pubtoaddr(pub)
|
||||
> addr
|
||||
'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'
|
||||
> h = history(addr)
|
||||
> h
|
||||
[{'output': u'97f7c7d8ac85e40c255f8a763b6cd9a68f3a94d2e93e8bfa08f977b92e55465e:0', 'value': 50000, 'address': u'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'}, {'output': u'4cc806bb04f730c445c60b3e0f4f44b54769a1c196ca37d8d4002135e4abd171:1', 'value': 50000, 'address': u'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'}]
|
||||
> outs = [{'value': 90000, 'address': '16iw1MQ1sy1DtRPYw3ao1bCamoyBJtRB4t'}]
|
||||
> tx = mktx(h,outs)
|
||||
> tx
|
||||
'01000000025e46552eb977f908fa8b3ee9d2943a8fa6d96c3b768a5f250ce485acd8c7f7970000000000ffffffff71d1abe4352100d4d837ca96c1a16947b5444f0f3e0bc645c430f704bb06c84c0100000000ffffffff01905f0100000000001976a9143ec6c3ed8dfc3ceabcc1cbdb0c5aef4e2d02873c88ac00000000'
|
||||
> tx2 = sign(tx,0,priv)
|
||||
> tx2
|
||||
'01000000025e46552eb977f908fa8b3ee9d2943a8fa6d96c3b768a5f250ce485acd8c7f797000000008b483045022100dd29d89a28451febb990fb1dafa21245b105140083ced315ebcdea187572b3990220713f2e554f384d29d7abfedf39f0eb92afba0ef46f374e49d43a728a0ff6046e01410420f34c2786b4bae593e22596631b025f3ff46e200fc1d4b52ef49bbdc2ed00b26c584b7e32523fb01be2294a1f8a5eb0cf71a203cc034ced46ea92a8df16c6e9ffffffff71d1abe4352100d4d837ca96c1a16947b5444f0f3e0bc645c430f704bb06c84c0100000000ffffffff01905f0100000000001976a9143ec6c3ed8dfc3ceabcc1cbdb0c5aef4e2d02873c88ac00000000'
|
||||
> tx3 = sign(tx2,1,priv)
|
||||
> tx3
|
||||
'01000000025e46552eb977f908fa8b3ee9d2943a8fa6d96c3b768a5f250ce485acd8c7f797000000008b483045022100dd29d89a28451febb990fb1dafa21245b105140083ced315ebcdea187572b3990220713f2e554f384d29d7abfedf39f0eb92afba0ef46f374e49d43a728a0ff6046e01410420f34c2786b4bae593e22596631b025f3ff46e200fc1d4b52ef49bbdc2ed00b26c584b7e32523fb01be2294a1f8a5eb0cf71a203cc034ced46ea92a8df16c6e9ffffffff71d1abe4352100d4d837ca96c1a16947b5444f0f3e0bc645c430f704bb06c84c010000008c4930460221008bbaaaf172adfefc3a1315dc7312c88645832ff76d52e0029d127e65bbeeabe1022100fdeb89658d503cf2737cedb4049e5070f689c50a9b6c85997d49e0787938f93901410420f34c2786b4bae593e22596631b025f3ff46e200fc1d4b52ef49bbdc2ed00b26c584b7e32523fb01be2294a1f8a5eb0cf71a203cc034ced46ea92a8df16c6e9ffffffff01905f0100000000001976a9143ec6c3ed8dfc3ceabcc1cbdb0c5aef4e2d02873c88ac00000000'
|
||||
> pushtx(tx3)
|
||||
'Transaction Submitted'
|
||||
|
||||
Or using the pybtctool command line interface:
|
||||
|
||||
@vub: pybtctool random_electrum_seed
|
||||
484ccb566edb66c65dd0fd2e4d90ef65
|
||||
|
||||
@vub: pybtctool electrum_privkey 484ccb566edb66c65dd0fd2e4d90ef65 0 0
|
||||
593240c2205e7b7b5d7c13393b7c9553497854b75c7470b76aeca50cd4a894d7
|
||||
|
||||
@vub: pybtctool electrum_mpk 484ccb566edb66c65dd0fd2e4d90ef65
|
||||
484e42865b8e9a6ea8262fd1cde666b557393258ed598d842e563ad9e5e6c70a97e387eefdef123c1b8b4eb21fe210c6216ad7cc1e4186fbbba70f0e2c062c25
|
||||
|
||||
@vub: pybtctool bip32_master_key 21456t243rhgtucyadh3wgyrcubw3grydfbng
|
||||
xprv9s21ZrQH143K2napkeoHT48gWmoJa89KCQj4nqLfdGybyWHP9Z8jvCGzuEDv4ihCyoed7RFPNbc9NxoSF7cAvH9AaNSvepUaeqbSpJZ4rbT
|
||||
|
||||
@vub: pybtctool bip32_ckd xprv9s21ZrQH143K2napkeoHT48gWmoJa89KCQj4nqLfdGybyWHP9Z8jvCGzuEDv4ihCyoed7RFPNbc9NxoSF7cAvH9AaNSvepUaeqbSpJZ4rbT 0
|
||||
xprv9vfzYrpwo7QHFdtrcvsSCTrBESFPUf1g7NRvayy1QkEfUekpDKLfqvHjgypF5w3nAvnwPjtQUNkyywWNkLbiUS95khfHCzJXFkLEdwRepbw
|
||||
|
||||
@vub: pybtctool bip32_privtopub xprv9s21ZrQH143K2napkeoHT48gWmoJa89KCQj4nqLfdGybyWHP9Z8jvCGzuEDv4ihCyoed7RFPNbc9NxoSF7cAvH9AaNSvepUaeqbSpJZ4rbT
|
||||
xpub661MyMwAqRbcFGfHrgLHpC5R4odnyasAZdefbDkHBcWarJcXh6SzTzbUkWuhnP142ZFdKdAJSuTSaiGDYjvm7bCLmA8DZqksYjJbYmcgrYF
|
||||
|
||||
The -s option lets you read arguments from the command line
|
||||
|
||||
@vub: pybtctool sha256 'some big long brainwallet password' | pybtctool -s privtoaddr | pybtctool -s history
|
||||
[{'output': u'97f7c7d8ac85e40c255f8a763b6cd9a68f3a94d2e93e8bfa08f977b92e55465e:0', 'value': 50000, 'address': u'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'}, {'output': u'4cc806bb04f730c445c60b3e0f4f44b54769a1c196ca37d8d4002135e4abd171:1', 'value': 50000, 'address': u'1CQLd3bhw4EzaURHbKCwM5YZbUQfA4ReY6'}]
|
||||
@vub: pybtctool random_electrum_seed | pybtctool -s electrum_privkey 0 0
|
||||
593240c2205e7b7b5d7c13393b7c9553497854b75c7470b76aeca50cd4a894d7
|
||||
|
||||
The -b option lets you read binary data as an argument
|
||||
|
||||
@vub: pybtctool sha256 123 | pybtctool -s changebase 16 256 | pybtctool -b changebase 256 16
|
||||
a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae30a
|
||||
|
||||
The -j option lets you read json from the command line (-J to split a json list into multiple arguments)
|
||||
|
||||
@vub: pybtctool unspent 1FxkfJQLJTXpW6QmxGT6oF43ZH959ns8Cq | pybtctool -j select 200000001 | pybtctool -j mksend 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P:20000 1FxkfJQLJTXpW6QmxGT6oF43ZH959ns8Cq 1000 | pybtctool -s signall 805cd74ca322633372b9bfb857f3be41db0b8de43a3c44353b238c0acff9d523
|
||||
0100000003d5001aae8358ae98cb02c1b6f9859dc1ac3dbc1e9cc88632afeb7b7e3c510a49000000008b4830450221009e03bb6122437767e2ca785535824f4ed13d2ebbb9fa4f9becc6d6f4e1e217dc022064577353c08d8d974250143d920d3b963b463e43bbb90f3371060645c49266b90141048ef80f6bd6b073407a69299c2ba89de48adb59bb9689a5ab040befbbebcfbb15d01b006a6b825121a0d2c546c277acb60f0bd3203bd501b8d67c7dba91f27f47ffffffff1529d655dff6a0f6c9815ee835312fb3ca4df622fde21b6b9097666e9284087d010000008a473044022035dd67d18b575ebd339d05ca6ffa1d27d7549bd993aeaf430985795459fc139402201aaa162cc50181cee493870c9479b1148243a33923cb77be44a73ca554a4e5d60141048ef80f6bd6b073407a69299c2ba89de48adb59bb9689a5ab040befbbebcfbb15d01b006a6b825121a0d2c546c277acb60f0bd3203bd501b8d67c7dba91f27f47ffffffff23d5f9cf0a8c233b35443c3ae48d0bdb41bef357b8bfb972336322a34cd75c80010000008b483045022014daa5c5bbe9b3e5f2539a5cd8e22ce55bc84788f946c5b3643ecac85b4591a9022100a4062074a1df3fa0aea5ef67368d0b1f0eaac520bee6e417c682d83cd04330450141048ef80f6bd6b073407a69299c2ba89de48adb59bb9689a5ab040befbbebcfbb15d01b006a6b825121a0d2c546c277acb60f0bd3203bd501b8d67c7dba91f27f47ffffffff02204e0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac5b3c4411000000001976a914a41d15ae657ad3bfd0846771a34d7584c37d54a288ac00000000
|
||||
|
||||
Fun stuff with json:
|
||||
|
||||
@vub: pybtctool history 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P | pybtctool -j multiaccess value | pybtctool -j sum
|
||||
625216206372
|
||||
|
||||
@vub: pybtctool history 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P | pybtctool -j count
|
||||
6198
|
||||
|
||||
### Listing of main commands:
|
||||
|
||||
* privkey_to_pubkey : (privkey) -> pubkey
|
||||
* privtopub : (privkey) -> pubkey
|
||||
* pubkey_to_address : (pubkey) -> address
|
||||
* pubtoaddr : (pubkey) -> address
|
||||
* privkey_to_address : (privkey) -> address
|
||||
* privtoaddr : (privkey) -> address
|
||||
|
||||
* add : (key1, key2) -> key1 + key2 (works on privkeys or pubkeys)
|
||||
* multiply : (pubkey, privkey) -> returns pubkey * privkey
|
||||
|
||||
* ecdsa_sign : (message, privkey) -> sig
|
||||
* ecdsa_verify : (message, sig, pubkey) -> True/False
|
||||
* ecdsa_recover : (message, sig) -> pubkey
|
||||
|
||||
* random_key : () -> privkey
|
||||
* random_electrum_seed : () -> electrum seed
|
||||
|
||||
* electrum_stretch : (seed) -> secret exponent
|
||||
* electrum_privkey : (seed or secret exponent, i, type) -> privkey
|
||||
* electrum_mpk : (seed or secret exponent) -> master public key
|
||||
* electrum_pubkey : (seed or secexp or mpk) -> pubkey
|
||||
|
||||
* bip32_master_key : (seed) -> bip32 master key
|
||||
* bip32_ckd : (private or public bip32 key, i) -> child key
|
||||
* bip32_privtopub : (private bip32 key) -> public bip32 key
|
||||
* bip32_extract_key : (private or public bip32_key) -> privkey or pubkey
|
||||
|
||||
* deserialize : (hex or bin transaction) -> JSON tx
|
||||
* serialize : (JSON tx) -> hex or bin tx
|
||||
* mktx : (inputs, outputs) -> tx
|
||||
* mksend : (inputs, outputs, change_addr, fee) -> tx
|
||||
* sign : (tx, i, privkey) -> tx with index i signed with privkey
|
||||
* multisign : (tx, i, script, privkey) -> signature
|
||||
* apply_multisignatures: (tx, i, script, sigs) -> tx with index i signed with sigs
|
||||
* scriptaddr : (script) -> P2SH address
|
||||
* mk_multisig_script : (pubkeys, k, n) -> k-of-n multisig script from pubkeys
|
||||
* verify_tx_input : (tx, i, script, sig, pub) -> True/False
|
||||
* tx_hash : (hex or bin tx) -> hash
|
||||
|
||||
* history : (address1, address2, etc) -> outputs to those addresses
|
||||
* unspent : (address1, address2, etc) -> unspent outputs to those addresses
|
||||
* fetchtx : (txash) -> tx if present
|
||||
* pushtx : (hex or bin tx) -> tries to push to blockchain.info/pushtx
|
||||
|
||||
* access : (json list/object, prop) -> desired property of that json object
|
||||
* multiaccess : (json list, prop) -> like access, but mapped across each list element
|
||||
* slice : (json list, start, end) -> given slice of the list
|
||||
* count : (json list) -> number of elements
|
||||
* sum : (json list) -> sum of all values
|
|
@ -1,10 +0,0 @@
|
|||
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 *
|
|
@ -1,528 +0,0 @@
|
|||
#!/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
|
|
@ -1,50 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
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)
|
|
@ -1,199 +0,0 @@
|
|||
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)
|
File diff suppressed because it is too large
Load diff
|
@ -1,581 +0,0 @@
|
|||
#!/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')
|
|
@ -1,127 +0,0 @@
|
|||
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"))
|
||||
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
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)
|
|
@ -1,123 +0,0 @@
|
|||
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))
|
|
@ -1,414 +0,0 @@
|
|||
## 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()
|
|
@ -1,100 +0,0 @@
|
|||
import main as main
|
||||
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:]
|
|
@ -1,514 +0,0 @@
|
|||
#!/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)
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
import sys, json, re
|
||||
from bitcoin import *
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
print "pybtctool <command> <arg1> <arg2> ..."
|
||||
else:
|
||||
cmdargs, preargs, kwargs = [], [], {}
|
||||
i = 2
|
||||
# Process first arg tag
|
||||
if sys.argv[1] == '-s':
|
||||
preargs.extend(re.findall(r'\S\S*', sys.stdin.read()))
|
||||
elif sys.argv[1] == '-B':
|
||||
preargs.extend([sys.stdin.read()])
|
||||
elif sys.argv[1] == '-b':
|
||||
preargs.extend([sys.stdin.read()[:-1]])
|
||||
elif sys.argv[1] == '-j':
|
||||
preargs.extend([json.loads(sys.stdin.read())])
|
||||
elif sys.argv[1] == '-J':
|
||||
preargs.extend(json.loads(sys.stdin.read()))
|
||||
else:
|
||||
i = 1
|
||||
while i < len(sys.argv):
|
||||
if sys.argv[i][:2] == '--':
|
||||
kwargs[sys.argv[i][2:]] = sys.argv[i+1]
|
||||
i += 2
|
||||
else:
|
||||
cmdargs.append(sys.argv[i])
|
||||
i += 1
|
||||
cmd = cmdargs[0]
|
||||
args = preargs + cmdargs[1:]
|
||||
o = vars()[cmd](*args, **kwargs)
|
||||
if isinstance(o, (list, dict)):
|
||||
print json.dumps(o)
|
||||
else:
|
||||
print o
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
setup(name='bitcoin',
|
||||
version='1.1.42',
|
||||
description='Python Bitcoin Tools',
|
||||
author='Vitalik Buterin',
|
||||
author_email='vbuterin@gmail.com',
|
||||
url='http://github.com/vbuterin/pybitcointools',
|
||||
packages=['bitcoin'],
|
||||
scripts=['pybtctool'],
|
||||
include_package_data=True,
|
||||
data_files=[("", ["LICENSE"]), ("bitcoin", ["bitcoin/english.txt"])],
|
||||
)
|
|
@ -1,496 +0,0 @@
|
|||
import json
|
||||
import os
|
||||
import random
|
||||
import unittest
|
||||
|
||||
import bitcoin.ripemd as ripemd
|
||||
from bitcoin import *
|
||||
|
||||
|
||||
class TestECCArithmetic(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print('Starting ECC arithmetic tests')
|
||||
|
||||
def test_all(self):
|
||||
for i in range(8):
|
||||
print('### Round %d' % (i+1))
|
||||
x, y = random.randrange(2**256), random.randrange(2**256)
|
||||
self.assertEqual(
|
||||
multiply(multiply(G, x), y)[0],
|
||||
multiply(multiply(G, y), x)[0]
|
||||
)
|
||||
self.assertEqual(
|
||||
|
||||
add_pubkeys(multiply(G, x), multiply(G, y))[0],
|
||||
multiply(G, add_privkeys(x, y))[0]
|
||||
)
|
||||
|
||||
hx, hy = encode(x % N, 16, 64), encode(y % N, 16, 64)
|
||||
self.assertEqual(
|
||||
multiply(multiply(G, hx), hy)[0],
|
||||
multiply(multiply(G, hy), hx)[0]
|
||||
)
|
||||
self.assertEqual(
|
||||
add_pubkeys(multiply(G, hx), multiply(G, hy))[0],
|
||||
multiply(G, add_privkeys(hx, hy))[0]
|
||||
)
|
||||
self.assertEqual(
|
||||
b58check_to_hex(pubtoaddr(privtopub(x))),
|
||||
b58check_to_hex(pubtoaddr(multiply(G, hx), 23))
|
||||
)
|
||||
|
||||
p = privtopub(sha256(str(x)))
|
||||
if i % 2 == 1:
|
||||
p = changebase(p, 16, 256)
|
||||
self.assertEqual(p, decompress(compress(p)))
|
||||
self.assertEqual(G[0], multiply(divide(G, x), x)[0])
|
||||
|
||||
|
||||
class TestBases(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print('Starting base change tests')
|
||||
|
||||
def test_all(self):
|
||||
data = [
|
||||
[10, '65535', 16, 'ffff'],
|
||||
[16, 'deadbeef', 10, '3735928559'],
|
||||
[10, '0', 16, ''],
|
||||
[256, b'34567', 10, '219919234615'],
|
||||
[10, '444', 16, '1bc'],
|
||||
[256, b'\x03\x04\x05\x06\x07', 10, '12952339975'],
|
||||
[16, '3132333435', 256, b'12345']
|
||||
]
|
||||
for prebase, preval, postbase, postval in data:
|
||||
self.assertEqual(changebase(preval, prebase, postbase), postval)
|
||||
|
||||
for i in range(100):
|
||||
x = random.randrange(1, 9999999999999999)
|
||||
frm = random.choice([2, 10, 16, 58, 256])
|
||||
to = random.choice([2, 10, 16, 58, 256])
|
||||
self.assertEqual(decode(encode(x, to), to), x)
|
||||
self.assertEqual(changebase(encode(x, frm), frm, to), encode(x, to))
|
||||
self.assertEqual(decode(changebase(encode(x, frm), frm, to), to), x)
|
||||
|
||||
|
||||
class TestElectrumWalletInternalConsistency(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print('Starting Electrum wallet internal consistency tests')
|
||||
|
||||
def test_all(self):
|
||||
for i in range(3):
|
||||
seed = sha256(str(random.randrange(2**40)))[:32]
|
||||
mpk = electrum_mpk(seed)
|
||||
for i in range(5):
|
||||
pk = electrum_privkey(seed, i)
|
||||
pub = electrum_pubkey((mpk, seed)[i % 2], i)
|
||||
pub2 = privtopub(pk)
|
||||
self.assertEqual(
|
||||
pub,
|
||||
pub2,
|
||||
'Does not match! Details:\nseed: %s\nmpk: %s\npriv: %s\npub: %s\npub2: %s' % (
|
||||
seed, mpk, pk, pub, pub2
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TestRawSignRecover(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print("Basic signing and recovery tests")
|
||||
|
||||
def test_all(self):
|
||||
for i in range(20):
|
||||
k = sha256(str(i))
|
||||
s = ecdsa_raw_sign('35' * 32, k)
|
||||
self.assertEqual(
|
||||
ecdsa_raw_recover('35' * 32, s),
|
||||
decode_pubkey(privtopub(k))
|
||||
)
|
||||
|
||||
|
||||
class TestTransactionSignVerify(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print("Transaction-style signing and verification tests")
|
||||
|
||||
def test_all(self):
|
||||
alphabet = "1234567890qwertyuiopasdfghjklzxcvbnm"
|
||||
for i in range(10):
|
||||
msg = ''.join([random.choice(alphabet) for i in range(random.randrange(20, 200))])
|
||||
priv = sha256(str(random.randrange(2**256)))
|
||||
pub = privtopub(priv)
|
||||
sig = ecdsa_tx_sign(msg, priv)
|
||||
self.assertTrue(
|
||||
ecdsa_tx_verify(msg, sig, pub),
|
||||
"Verification error"
|
||||
)
|
||||
|
||||
self.assertIn(
|
||||
pub,
|
||||
ecdsa_tx_recover(msg, sig),
|
||||
"Recovery failed"
|
||||
)
|
||||
|
||||
|
||||
class TestSerialize(unittest.TestCase):
|
||||
|
||||
def test_serialize(self):
|
||||
tx = '0100000001239f932c780e517015842f3b02ff765fba97f9f63f9f1bc718b686a56ed9c73400000000fd5d010047304402200c40fa58d3f6d5537a343cf9c8d13bc7470baf1d13867e0de3e535cd6b4354c802200f2b48f67494835b060d0b2ff85657d2ba2d9ea4e697888c8cb580e8658183a801483045022056f488c59849a4259e7cef70fe5d6d53a4bd1c59a195b0577bd81cb76044beca022100a735b319fa66af7b178fc719b93f905961ef4d4446deca8757a90de2106dd98a014cc95241046c7d87fd72caeab48e937f2feca9e9a4bd77f0eff4ebb2dbbb9855c023e334e188d32aaec4632ea4cbc575c037d8101aec73d029236e7b1c2380f3e4ad7edced41046fd41cddf3bbda33a240b417a825cc46555949917c7ccf64c59f42fd8dfe95f34fae3b09ed279c8c5b3530510e8cca6230791102eef9961d895e8db54af0563c410488d618b988efd2511fc1f9c03f11c210808852b07fe46128c1a6b1155aa22cdf4b6802460ba593db2d11c7e6cbe19cedef76b7bcabd05d26fd97f4c5a59b225053aeffffffff0310270000000000001976a914a89733100315c37d228a529853af341a9d290a4588ac409c00000000000017a9142b56f9a4009d9ff99b8f97bea4455cd71135f5dd87409c00000000000017a9142b56f9a4009d9ff99b8f97bea4455cd71135f5dd8700000000'
|
||||
self.assertEqual(
|
||||
serialize(deserialize(tx)),
|
||||
tx,
|
||||
"Serialize roundtrip failed"
|
||||
)
|
||||
|
||||
def test_serialize_script(self):
|
||||
script = '47304402200c40fa58d3f6d5537a343cf9c8d13bc7470baf1d13867e0de3e535cd6b4354c802200f2b48f67494835b060d0b2ff85657d2ba2d9ea4e697888c8cb580e8658183a801483045022056f488c59849a4259e7cef70fe5d6d53a4bd1c59a195b0577bd81cb76044beca022100a735b319fa66af7b178fc719b93f905961ef4d4446deca8757a90de2106dd98a014cc95241046c7d87fd72caeab48e937f2feca9e9a4bd77f0eff4ebb2dbbb9855c023e334e188d32aaec4632ea4cbc575c037d8101aec73d029236e7b1c2380f3e4ad7edced41046fd41cddf3bbda33a240b417a825cc46555949917c7ccf64c59f42fd8dfe95f34fae3b09ed279c8c5b3530510e8cca6230791102eef9961d895e8db54af0563c410488d618b988efd2511fc1f9c03f11c210808852b07fe46128c1a6b1155aa22cdf4b6802460ba593db2d11c7e6cbe19cedef76b7bcabd05d26fd97f4c5a59b225053ae'
|
||||
self.assertEqual(
|
||||
serialize_script(deserialize_script(script)),
|
||||
script,
|
||||
"Script serialize roundtrip failed"
|
||||
)
|
||||
|
||||
|
||||
class TestTransaction(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print("Attempting transaction creation")
|
||||
|
||||
# FIXME: I don't know how to write this as a unit test.
|
||||
# What should be asserted?
|
||||
def test_all(self):
|
||||
privs = [sha256(str(random.randrange(2**256))) for x in range(4)]
|
||||
pubs = [privtopub(priv) for priv in privs]
|
||||
addresses = [pubtoaddr(pub) for pub in pubs]
|
||||
mscript = mk_multisig_script(pubs[1:], 2, 3)
|
||||
msigaddr = p2sh_scriptaddr(mscript)
|
||||
tx = mktx(['01'*32+':1', '23'*32+':2'], [msigaddr+':20202', addresses[0]+':40404'])
|
||||
tx1 = sign(tx, 1, privs[0])
|
||||
|
||||
sig1 = multisign(tx, 0, mscript, privs[1])
|
||||
self.assertTrue(verify_tx_input(tx1, 0, mscript, sig1, pubs[1]), "Verification Error")
|
||||
|
||||
sig3 = multisign(tx, 0, mscript, privs[3])
|
||||
self.assertTrue(verify_tx_input(tx1, 0, mscript, sig3, pubs[3]), "Verification Error")
|
||||
|
||||
tx2 = apply_multisignatures(tx1, 0, mscript, [sig1, sig3])
|
||||
print("Outputting transaction: ", tx2)
|
||||
|
||||
# https://github.com/vbuterin/pybitcointools/issues/71
|
||||
def test_multisig(self):
|
||||
script = mk_multisig_script(["0254236f7d1124fc07600ad3eec5ac47393bf963fbf0608bcce255e685580d16d9",
|
||||
"03560cad89031c412ad8619398bd43b3d673cb5bdcdac1afc46449382c6a8e0b2b"],
|
||||
2)
|
||||
|
||||
self.assertEqual(p2sh_scriptaddr(script), "33byJBaS5N45RHFcatTSt9ZjiGb6nK4iV3")
|
||||
|
||||
self.assertEqual(p2sh_scriptaddr(script, 0x05), "33byJBaS5N45RHFcatTSt9ZjiGb6nK4iV3")
|
||||
self.assertEqual(p2sh_scriptaddr(script, 5), "33byJBaS5N45RHFcatTSt9ZjiGb6nK4iV3")
|
||||
|
||||
self.assertEqual(p2sh_scriptaddr(script, 0xc4), "2MuABMvWTgpZRd4tAG25KW6YzvcoGVZDZYP")
|
||||
self.assertEqual(p2sh_scriptaddr(script, 196), "2MuABMvWTgpZRd4tAG25KW6YzvcoGVZDZYP")
|
||||
|
||||
|
||||
class TestDeterministicGenerate(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print("Beginning RFC6979 deterministic signing tests")
|
||||
|
||||
def test_all(self):
|
||||
# Created with python-ecdsa 0.9
|
||||
# Code to make your own vectors:
|
||||
# class gen:
|
||||
# def order(self): return 115792089237316195423570985008687907852837564279074904382605163141518161494337
|
||||
# dummy = gen()
|
||||
# for i in range(10): ecdsa.rfc6979.generate_k(dummy, i, hashlib.sha256, hashlib.sha256(str(i)).digest())
|
||||
test_vectors = [
|
||||
32783320859482229023646250050688645858316445811207841524283044428614360139869,
|
||||
109592113955144883013243055602231029997040992035200230706187150761552110229971,
|
||||
65765393578006003630736298397268097590176526363988568884298609868706232621488,
|
||||
85563144787585457107933685459469453513056530050186673491900346620874099325918,
|
||||
99829559501561741463404068005537785834525504175465914981205926165214632019533,
|
||||
7755945018790142325513649272940177083855222863968691658328003977498047013576,
|
||||
81516639518483202269820502976089105897400159721845694286620077204726637043798,
|
||||
52824159213002398817852821148973968315579759063230697131029801896913602807019,
|
||||
44033460667645047622273556650595158811264350043302911918907282441675680538675,
|
||||
32396602643737403620316035551493791485834117358805817054817536312402837398361
|
||||
]
|
||||
|
||||
for i, ti in enumerate(test_vectors):
|
||||
mine = deterministic_generate_k(bin_sha256(str(i)), encode(i, 256, 32))
|
||||
self.assertEqual(
|
||||
ti,
|
||||
mine,
|
||||
"Test vector does not match. Details:\n%s\n%s" % (
|
||||
ti,
|
||||
mine
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TestBIP0032(unittest.TestCase):
|
||||
"""See: https://en.bitcoin.it/wiki/BIP_0032"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print("Beginning BIP0032 tests")
|
||||
|
||||
def _full_derive(self, key, chain):
|
||||
if len(chain) == 0:
|
||||
return key
|
||||
elif chain[0] == 'pub':
|
||||
return self._full_derive(bip32_privtopub(key), chain[1:])
|
||||
else:
|
||||
return self._full_derive(bip32_ckd(key, chain[0]), chain[1:])
|
||||
|
||||
def test_all(self):
|
||||
test_vectors = [
|
||||
[[], 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'],
|
||||
[['pub'], 'xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8'],
|
||||
[[2**31], 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7'],
|
||||
[[2**31, 1], 'xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs'],
|
||||
[[2**31, 1, 2**31 + 2], 'xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM'],
|
||||
[[2**31, 1, 2**31 + 2, 'pub', 2, 1000000000], 'xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy']
|
||||
]
|
||||
|
||||
mk = bip32_master_key(safe_from_hex('000102030405060708090a0b0c0d0e0f'))
|
||||
|
||||
for tv in test_vectors:
|
||||
left, right = self._full_derive(mk, tv[0]), tv[1]
|
||||
self.assertEqual(
|
||||
left,
|
||||
right,
|
||||
r"Test vector does not match. Details: \n%s\n%s\n\%s" % (
|
||||
tv[0],
|
||||
[x.encode('hex') if isinstance(x, str) else x for x in bip32_deserialize(left)],
|
||||
[x.encode('hex') if isinstance(x, str) else x for x in bip32_deserialize(right)],
|
||||
)
|
||||
)
|
||||
|
||||
def test_all_testnet(self):
|
||||
test_vectors = [
|
||||
[[], 'tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m'],
|
||||
[['pub'], 'tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp'],
|
||||
[[2**31], 'tprv8bxNLu25VazNnppTCP4fyhyCvBHcYtzE3wr3cwYeL4HA7yf6TLGEUdS4QC1vLT63TkjRssqJe4CvGNEC8DzW5AoPUw56D1Ayg6HY4oy8QZ9'],
|
||||
[[2**31, 1], 'tprv8e8VYgZxtHsSdGrtvdxYaSrryZGiYviWzGWtDDKTGh5NMXAEB8gYSCLHpFCywNs5uqV7ghRjimALQJkRFZnUrLHpzi2pGkwqLtbubgWuQ8q'],
|
||||
[[2**31, 1, 2**31 + 2], 'tprv8gjmbDPpbAirVSezBEMuwSu1Ci9EpUJWKokZTYccSZSomNMLytWyLdtDNHRbucNaRJWWHANf9AzEdWVAqahfyRjVMKbNRhBmxAM8EJr7R15'],
|
||||
[[2**31, 1, 2**31 + 2, 'pub', 2, 1000000000], 'tpubDHNy3kAG39ThyiwwsgoKY4iRenXDRtce8qdCFJZXPMCJg5dsCUHayp84raLTpvyiNA9sXPob5rgqkKvkN8S7MMyXbnEhGJMW64Cf4vFAoaF']
|
||||
]
|
||||
|
||||
mk = bip32_master_key(safe_from_hex('000102030405060708090a0b0c0d0e0f'), TESTNET_PRIVATE)
|
||||
|
||||
for tv in test_vectors:
|
||||
left, right = self._full_derive(mk, tv[0]), tv[1]
|
||||
self.assertEqual(
|
||||
left,
|
||||
right,
|
||||
r"Test vector does not match. Details:\n%s\n%s\n%s\n\%s" % (
|
||||
left,
|
||||
tv[0],
|
||||
[x.encode('hex') if isinstance(x, str) else x for x in bip32_deserialize(left)],
|
||||
[x.encode('hex') if isinstance(x, str) else x for x in bip32_deserialize(right)],
|
||||
)
|
||||
)
|
||||
|
||||
def test_extra(self):
|
||||
master = bip32_master_key(safe_from_hex("000102030405060708090a0b0c0d0e0f"))
|
||||
|
||||
# m/0
|
||||
assert bip32_ckd(master, "0") == "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R"
|
||||
assert bip32_privtopub(bip32_ckd(master, "0")) == "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1"
|
||||
|
||||
# m/1
|
||||
assert bip32_ckd(master, "1") == "xprv9uHRZZhbkedL4yTpidDvuVfrdUkTbhDHviERRBkbzbNDZeMjWzqzKAdxWhzftGDSxDmBdakjqHiZJbkwiaTEXJdjZAaAjMZEE3PMbMrPJih"
|
||||
assert bip32_privtopub(bip32_ckd(master, "1")) == "xpub68Gmy5EVb2BdHTYHpekwGdcbBWax19w9HwA2DaADYvuCSSgt4YAErxxSN1KWSnmyqkwRNbnTj3XiUBKmHeC8rTjLRPjSULcDKQQgfgJDppq"
|
||||
|
||||
# m/0/0
|
||||
assert bip32_ckd(bip32_ckd(master, "0"), "0") == "xprv9ww7sMFLzJMzur2oEQDB642fbsMS4q6JRraMVTrM9bTWBq7NDS8ZpmsKVB4YF3mZecqax1fjnsPF19xnsJNfRp4RSyexacULXMKowSACTRc"
|
||||
assert bip32_privtopub(bip32_ckd(bip32_ckd(master, "0"), "0")) == "xpub6AvUGrnEpfvJ8L7GLRkBTByQ9uBvUHp9o5VxHrFxhvzV4dSWkySpNaBoLR9FpbnwRmTa69yLHF3QfcaxbWT7gWdwws5k4dpmJvqpEuMWwnj"
|
||||
|
||||
# m/0'
|
||||
assert bip32_ckd(master, 2**31) == "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"
|
||||
assert bip32_privtopub(bip32_ckd(master, 2**31)) == "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
|
||||
|
||||
# m/1'
|
||||
assert bip32_ckd(master, 2**31 + 1) == "xprv9uHRZZhk6KAJFszJGW6LoUFq92uL7FvkBhmYiMurCWPHLJZkX2aGvNdRUBNnJu7nv36WnwCN59uNy6sxLDZvvNSgFz3TCCcKo7iutQzpg78"
|
||||
assert bip32_privtopub(bip32_ckd(master, 2**31 + 1)) == "xpub68Gmy5EdvgibUN4mNXdMAcCZh4jpWiebYvh9WkKTkqvGD6tu4ZtXUAwuKSyF5DFZVmotf9UHFTGqSXo9qyDBSn47RkaN6Aedt9JbL7zcgSL"
|
||||
|
||||
# m/1'
|
||||
assert bip32_ckd(master, 1 + 2**31) == "xprv9uHRZZhk6KAJFszJGW6LoUFq92uL7FvkBhmYiMurCWPHLJZkX2aGvNdRUBNnJu7nv36WnwCN59uNy6sxLDZvvNSgFz3TCCcKo7iutQzpg78"
|
||||
assert bip32_privtopub(bip32_ckd(master, 1 + 2**31)) == "xpub68Gmy5EdvgibUN4mNXdMAcCZh4jpWiebYvh9WkKTkqvGD6tu4ZtXUAwuKSyF5DFZVmotf9UHFTGqSXo9qyDBSn47RkaN6Aedt9JbL7zcgSL"
|
||||
|
||||
# m/0'/0
|
||||
assert bip32_ckd(bip32_ckd(master, 2**31), "0") == "xprv9wTYmMFdV23N21MM6dLNavSQV7Sj7meSPXx6AV5eTdqqGLjycVjb115Ec5LgRAXscPZgy5G4jQ9csyyZLN3PZLxoM1h3BoPuEJzsgeypdKj"
|
||||
assert bip32_privtopub(bip32_ckd(bip32_ckd(master, 2**31), "0")) == "xpub6ASuArnXKPbfEVRpCesNx4P939HDXENHkksgxsVG1yNp9958A33qYoPiTN9QrJmWFa2jNLdK84bWmyqTSPGtApP8P7nHUYwxHPhqmzUyeFG"
|
||||
|
||||
# m/0'/0'
|
||||
assert bip32_ckd(bip32_ckd(master, 2**31), 2**31) == "xprv9wTYmMFmpgaLB5Hge4YtaGqCKpsYPTD9vXWSsmdZrNU3Y2i4WoBykm6ZteeCLCCZpGxdHQuqEhM6Gdo2X6CVrQiTw6AAneF9WSkA9ewaxtS"
|
||||
assert bip32_privtopub(bip32_ckd(bip32_ckd(master, 2**31), 2**31)) == "xpub6ASuArnff48dPZN9k65twQmvsri2nuw1HkS3gA3BQi12Qq3D4LWEJZR3jwCAr1NhsFMcQcBkmevmub6SLP37bNq91SEShXtEGUbX3GhNaGk"
|
||||
|
||||
# m/44'/0'/0'/0/0
|
||||
assert bip32_ckd(bip32_ckd(bip32_ckd(bip32_ckd(bip32_ckd(master, 44 + 2**31), 2**31), 2**31), 0), 0) == "xprvA4A9CuBXhdBtCaLxwrw64Jaran4n1rgzeS5mjH47Ds8V67uZS8tTkG8jV3BZi83QqYXPcN4v8EjK2Aof4YcEeqLt688mV57gF4j6QZWdP9U"
|
||||
assert bip32_privtopub(bip32_ckd(bip32_ckd(bip32_ckd(bip32_ckd(bip32_ckd(master, 44 + 2**31), 2**31), 2**31), 0), 0)) == "xpub6H9VcQiRXzkBR4RS3tU6RSXb8ouGRKQr1f1NXfTinCfTxvEhygCiJ4TDLHz1dyQ6d2Vz8Ne7eezkrViwaPo2ZMsNjVtFwvzsQXCDV6HJ3cV"
|
||||
|
||||
|
||||
class TestStartingAddressAndScriptGenerationConsistency(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print("Starting address and script generation consistency tests")
|
||||
|
||||
def test_all(self):
|
||||
for i in range(5):
|
||||
a = privtoaddr(random_key())
|
||||
self.assertEqual(a, script_to_address(address_to_script(a)))
|
||||
self.assertEqual(a, script_to_address(address_to_script(a), 0))
|
||||
self.assertEqual(a, script_to_address(address_to_script(a), 0x00))
|
||||
|
||||
b = privtoaddr(random_key(), 5)
|
||||
self.assertEqual(b, script_to_address(address_to_script(b)))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 0))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 0x00))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 5))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 0x05))
|
||||
|
||||
|
||||
for i in range(5):
|
||||
a = privtoaddr(random_key(), 0x6f)
|
||||
self.assertEqual(a, script_to_address(address_to_script(a), 111))
|
||||
self.assertEqual(a, script_to_address(address_to_script(a), 0x6f))
|
||||
|
||||
b = privtoaddr(random_key(), 0xc4)
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 111))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 0x6f))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 196))
|
||||
self.assertEqual(b, script_to_address(address_to_script(b), 0xc4))
|
||||
|
||||
|
||||
class TestRipeMD160PythonBackup(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print('Testing the pure python backup for ripemd160')
|
||||
|
||||
def test_all(self):
|
||||
strvec = [
|
||||
'',
|
||||
'The quick brown fox jumps over the lazy dog',
|
||||
'The quick brown fox jumps over the lazy cog',
|
||||
'Nobody inspects the spammish repetition'
|
||||
]
|
||||
|
||||
target = [
|
||||
'9c1185a5c5e9fc54612808977ee8f548b2258d31',
|
||||
'37f332f68db77bd9d7edd4969571ad671cf9dd3b',
|
||||
'132072df690933835eb8b6ad0b77e7b6f14acad7',
|
||||
'cc4a5ce1b3df48aec5d22d1f16b894a0b894eccc'
|
||||
]
|
||||
|
||||
hash160target = [
|
||||
'b472a266d0bd89c13706a4132ccfb16f7c3b9fcb',
|
||||
'0e3397b4abc7a382b3ea2365883c3c7ca5f07600',
|
||||
'53e0dacac5249e46114f65cb1f30d156b14e0bdc',
|
||||
'1c9b7b48049a8f98699bca22a5856c5ef571cd68'
|
||||
]
|
||||
|
||||
for i, s in enumerate(strvec):
|
||||
digest = ripemd.RIPEMD160(s).digest()
|
||||
hash160digest = ripemd.RIPEMD160(bin_sha256(s)).digest()
|
||||
self.assertEqual(bytes_to_hex_string(digest), target[i])
|
||||
self.assertEqual(bytes_to_hex_string(hash160digest), hash160target[i])
|
||||
self.assertEqual(bytes_to_hex_string(bin_hash160(from_string_to_bytes(s))), hash160target[i])
|
||||
self.assertEqual(hash160(from_string_to_bytes(s)), hash160target[i])
|
||||
|
||||
|
||||
class TestScriptVsAddressOutputs(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
print('Testing script vs address outputs')
|
||||
|
||||
def test_all(self):
|
||||
addr0 = '1Lqgj1ThNfwLgHMp5qJUerYsuUEm8vHmVG'
|
||||
script0 = '76a914d99f84267d1f90f3e870a5e9d2399918140be61d88ac'
|
||||
addr1 = '31oSGBBNrpCiENH3XMZpiP6GTC4tad4bMy'
|
||||
script1 = 'a9140136d001619faba572df2ef3d193a57ad29122d987'
|
||||
|
||||
inputs = [{
|
||||
'output': 'cd6219ea108119dc62fce09698b649efde56eca7ce223a3315e8b431f6280ce7:0',
|
||||
'value': 158000
|
||||
}]
|
||||
|
||||
outputs = [
|
||||
[{'address': addr0, 'value': 1000}, {'address': addr1, 'value': 2000}],
|
||||
[{'script': script0, 'value': 1000}, {'address': addr1, 'value': 2000}],
|
||||
[{'address': addr0, 'value': 1000}, {'script': script1, 'value': 2000}],
|
||||
[{'script': script0, 'value': 1000}, {'script': script1, 'value': 2000}],
|
||||
[addr0 + ':1000', addr1 + ':2000'],
|
||||
[script0 + ':1000', addr1 + ':2000'],
|
||||
[addr0 + ':1000', script1 + ':2000'],
|
||||
[script0 + ':1000', script1 + ':2000']
|
||||
]
|
||||
|
||||
for outs in outputs:
|
||||
tx_struct = deserialize(mktx(inputs, outs))
|
||||
self.assertEqual(tx_struct['outs'], outputs[3])
|
||||
|
||||
|
||||
class TestConversions(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.privkey_hex = (
|
||||
"e9873d79c6d87dc0fb6a5778633389f4453213303da61f20bd67fc233aa33262"
|
||||
)
|
||||
cls.privkey_bin = (
|
||||
b"\xe9\x87=y\xc6\xd8}\xc0\xfbjWxc3\x89\xf4E2\x130=\xa6\x1f \xbdg\xfc#:\xa32b"
|
||||
)
|
||||
|
||||
cls.pubkey_hex = (
|
||||
"04588d202afcc1ee4ab5254c7847ec25b9a135bbda0f2bc69ee1a714749fd77dc9f88ff2a00d7e752d44cbe16e1ebcf0890b76ec7c78886109dee76ccfc8445424"
|
||||
)
|
||||
cls.pubkey_bin = (
|
||||
b"\x04X\x8d *\xfc\xc1\xeeJ\xb5%LxG\xec%\xb9\xa15\xbb\xda\x0f+\xc6\x9e\xe1\xa7\x14t\x9f\xd7}\xc9\xf8\x8f\xf2\xa0\r~u-D\xcb\xe1n\x1e\xbc\xf0\x89\x0bv\xec|x\x88a\t\xde\xe7l\xcf\xc8DT$"
|
||||
)
|
||||
|
||||
def test_privkey_to_pubkey(self):
|
||||
pubkey_hex = privkey_to_pubkey(self.privkey_hex)
|
||||
self.assertEqual(pubkey_hex, self.pubkey_hex)
|
||||
|
||||
def test_changebase(self):
|
||||
self.assertEqual(
|
||||
self.pubkey_bin,
|
||||
changebase(
|
||||
self.pubkey_hex, 16, 256, minlen=len(self.pubkey_bin)
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.pubkey_hex,
|
||||
changebase(
|
||||
self.pubkey_bin, 256, 16, minlen=len(self.pubkey_hex)
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.privkey_bin,
|
||||
changebase(
|
||||
self.privkey_hex, 16, 256, minlen=len(self.privkey_bin)
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.privkey_hex,
|
||||
changebase(
|
||||
self.privkey_bin, 256, 16, minlen=len(self.privkey_hex)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -1,92 +0,0 @@
|
|||
import bitcoin as bc
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
class TestStealth(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
if sys.getrecursionlimit() < 1000:
|
||||
sys.setrecursionlimit(1000)
|
||||
|
||||
self.addr = 'vJmtjxSDxNPXL4RNapp9ARdqKz3uJyf1EDGjr1Fgqs9c8mYsVH82h8wvnA4i5rtJ57mr3kor1EVJrd4e5upACJd588xe52yXtzumxj'
|
||||
self.scan_pub = '025e58a31122b38c86abc119b9379fe247410aee87a533f9c07b189aef6c3c1f52'
|
||||
self.scan_priv = '3e49e7257cb31db997edb1cf8299af0f37e2663e2260e4b8033e49d39a6d02f2'
|
||||
self.spend_pub = '03616562c98e7d7b74be409a787cec3a912122f3fb331a9bee9b0b73ce7b9f50af'
|
||||
self.spend_priv = 'aa3db0cfb3edc94de4d10f873f8190843f2a17484f6021a95a7742302c744748'
|
||||
self.ephem_pub = '03403d306ec35238384c7e340393335f9bc9bb4a2e574eb4e419452c4ea19f14b0'
|
||||
self.ephem_priv = '9e63abaf8dcd5ea3919e6de0b6c544e00bf51bf92496113a01d6e369944dc091'
|
||||
self.shared_secret = 'a4047ee231f4121e3a99a3a3378542e34a384b865a9917789920e1f13ffd91c6'
|
||||
self.pay_pub = '02726112ad39cb6bf848b1b1ef30b88e35286bf99f746c2be575f96c0e02a9357c'
|
||||
self.pay_priv = '4e422fb1e5e1db6c1f6ab32a7706d368ceb385e7fab098e633c5c5949c3b97cd'
|
||||
|
||||
self.testnet_addr = 'waPUuLLykSnY3itzf1AyrQZm42F7KyB7SR5zpfqmnzPXWhx9kXLzV3EcyqzDdpTwngiyCCMUqztS9S1d7XJs3JMt3MsHPDpBCudvx9'
|
||||
|
||||
def test_address_encoding(self):
|
||||
|
||||
sc_pub, sp_pub = bc.basic_stealth_address_to_pubkeys(self.addr)
|
||||
self.assertEqual(sc_pub, self.scan_pub)
|
||||
self.assertEqual(sp_pub, self.spend_pub)
|
||||
|
||||
stealth_addr2 = bc.pubkeys_to_basic_stealth_address(sc_pub, sp_pub)
|
||||
self.assertEqual(stealth_addr2, self.addr)
|
||||
|
||||
magic_byte_testnet = 43
|
||||
sc_pub, sp_pub = bc.basic_stealth_address_to_pubkeys(self.testnet_addr)
|
||||
self.assertEqual(sc_pub, self.scan_pub)
|
||||
self.assertEqual(sp_pub, self.spend_pub)
|
||||
|
||||
stealth_addr2 = bc.pubkeys_to_basic_stealth_address(sc_pub, sp_pub, magic_byte_testnet)
|
||||
self.assertEqual(stealth_addr2, self.testnet_addr)
|
||||
|
||||
def test_shared_secret(self):
|
||||
|
||||
sh_sec = bc.shared_secret_sender(self.scan_pub, self.ephem_priv)
|
||||
self.assertEqual(sh_sec, self.shared_secret)
|
||||
|
||||
sh_sec2 = bc.shared_secret_receiver(self.ephem_pub, self.scan_priv)
|
||||
self.assertEqual(sh_sec2, self.shared_secret)
|
||||
|
||||
def test_uncover_pay_keys(self):
|
||||
|
||||
pub = bc.uncover_pay_pubkey_sender(self.scan_pub, self.spend_pub, self.ephem_priv)
|
||||
pub2 = bc.uncover_pay_pubkey_receiver(self.scan_priv, self.spend_pub, self.ephem_pub)
|
||||
self.assertEqual(pub, self.pay_pub)
|
||||
self.assertEqual(pub2, self.pay_pub)
|
||||
|
||||
priv = bc.uncover_pay_privkey(self.scan_priv, self.spend_priv, self.ephem_pub)
|
||||
self.assertEqual(priv, self.pay_priv)
|
||||
|
||||
def test_stealth_metadata_script(self):
|
||||
|
||||
nonce = int('deadbeef', 16)
|
||||
script = bc.mk_stealth_metadata_script(self.ephem_pub, nonce)
|
||||
self.assertEqual(script[6:], 'deadbeef' + self.ephem_pub)
|
||||
|
||||
eph_pub = bc.ephem_pubkey_from_tx_script(script)
|
||||
self.assertEqual(eph_pub, self.ephem_pub)
|
||||
|
||||
def test_stealth_tx_outputs(self):
|
||||
|
||||
nonce = int('deadbeef', 16)
|
||||
value = 10**8
|
||||
outputs = bc.mk_stealth_tx_outputs(self.addr, value, self.ephem_priv, nonce)
|
||||
|
||||
self.assertEqual(outputs[0]['value'], 0)
|
||||
self.assertEqual(outputs[0]['script'], '6a2606deadbeef' + self.ephem_pub)
|
||||
self.assertEqual(outputs[1]['address'], bc.pubkey_to_address(self.pay_pub))
|
||||
self.assertEqual(outputs[1]['value'], value)
|
||||
|
||||
outputs = bc.mk_stealth_tx_outputs(self.testnet_addr, value, self.ephem_priv, nonce, 'testnet')
|
||||
|
||||
self.assertEqual(outputs[0]['value'], 0)
|
||||
self.assertEqual(outputs[0]['script'], '6a2606deadbeef' + self.ephem_pub)
|
||||
self.assertEqual(outputs[1]['address'], bc.pubkey_to_address(self.pay_pub, 111))
|
||||
self.assertEqual(outputs[1]['value'], value)
|
||||
|
||||
self.assertRaises(Exception, bc.mk_stealth_tx_outputs, self.testnet_addr, value, self.ephem_priv, nonce, 'btc')
|
||||
|
||||
self.assertRaises(Exception, bc.mk_stealth_tx_outputs, self.addr, value, self.ephem_priv, nonce, 'testnet')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Add table
Add a link
Reference in a new issue