diff --git a/docs/_build/.DS_Store b/docs/_build/.DS_Store deleted file mode 100644 index 2c3e9ff..0000000 Binary files a/docs/_build/.DS_Store and /dev/null differ diff --git a/docs/build/doctrees/address.doctree b/docs/build/doctrees/address.doctree new file mode 100644 index 0000000..798b958 Binary files /dev/null and b/docs/build/doctrees/address.doctree differ diff --git a/docs/build/doctrees/block.doctree b/docs/build/doctrees/block.doctree new file mode 100644 index 0000000..d7323e1 Binary files /dev/null and b/docs/build/doctrees/block.doctree differ diff --git a/docs/build/doctrees/classes.doctree b/docs/build/doctrees/classes.doctree new file mode 100644 index 0000000..76d2057 Binary files /dev/null and b/docs/build/doctrees/classes.doctree differ diff --git a/docs/build/doctrees/contributing.doctree b/docs/build/doctrees/contributing.doctree new file mode 100644 index 0000000..f95cc19 Binary files /dev/null and b/docs/build/doctrees/contributing.doctree differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle new file mode 100644 index 0000000..75e1d8d Binary files /dev/null and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/examples.doctree b/docs/build/doctrees/examples.doctree new file mode 100644 index 0000000..21bc073 Binary files /dev/null and b/docs/build/doctrees/examples.doctree differ diff --git a/docs/build/doctrees/functional.doctree b/docs/build/doctrees/functional.doctree new file mode 100644 index 0000000..695a060 Binary files /dev/null and b/docs/build/doctrees/functional.doctree differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree new file mode 100644 index 0000000..c34f012 Binary files /dev/null and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/doctrees/installation.doctree b/docs/build/doctrees/installation.doctree new file mode 100644 index 0000000..86f153b Binary files /dev/null and b/docs/build/doctrees/installation.doctree differ diff --git a/docs/build/doctrees/transaction.doctree b/docs/build/doctrees/transaction.doctree new file mode 100644 index 0000000..eff737f Binary files /dev/null and b/docs/build/doctrees/transaction.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 0000000..5a69091 --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 8a461758623c87f8533d384a9a07b566 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/.nojekyll b/docs/build/html/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html new file mode 100644 index 0000000..a3a5074 --- /dev/null +++ b/docs/build/html/_modules/index.html @@ -0,0 +1,120 @@ + + + + +
+ + +
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_modules/pybtc/address.html b/docs/build/html/_modules/pybtc/address.html
new file mode 100644
index 0000000..8dd34be
--- /dev/null
+++ b/docs/build/html/_modules/pybtc/address.html
@@ -0,0 +1,337 @@
+
+
+
+
+
+
+
+
+from .tools import *
+
+
+[docs]class PrivateKey():
+ """
+ The class for creating private key object.
+
+ :param key: (optional) private key in HEX, bytes string or WIF format. In case no key specified
+ new random private key will be created.
+ :param compressed: (optional) if set to True private key corresponding compressed public key,
+ by default set to True. Recommended use only compressed public key.
+ :param testnet: (optional) if set to True mean that this private key for testnet Bitcoin network.
+
+ """
+ def __init__(self, key=None, compressed=True, testnet=False):
+
+ if key is None:
+
+ #: flag for compressed type of corresponding public key (boolean)
+ self.compressed = compressed
+ #: flag for testnet network private key (boolean)
+ self.testnet = testnet
+
+ #: private key in bytes (bytes)
+ self.key = create_private_key(wif=False)
+ #: private key in HEX (string)
+ self.hex = hexlify(self.key).decode()
+ #: private key in WIF format (string)
+ self.wif = private_key_to_wif(self.key, compressed, testnet)
+
+ else:
+ if isinstance(key, str):
+ try:
+ key = unhexlify(key)
+ except:
+ pass
+ if isinstance(key, bytes):
+ if len(key) != 32:
+ raise TypeError("private key invalid length")
+ self.key = key
+ self.compressed = compressed
+ self.testnet = testnet
+ self.hex = hexlify(self.key).decode()
+ self.wif = private_key_to_wif(self.key, compressed, testnet)
+ return
+ assert isinstance(key, str)
+ self.key = wif_to_private_key(key, hex=False)
+ self.hex = hexlify(self.key).decode()
+ self.wif = private_key_to_wif(self.key, compressed, testnet)
+ if key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
+ TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
+ self.compressed = False
+ else:
+ self.compressed = True
+ if key[0] in (TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
+ TESTNET_PRIVATE_KEY_COMPRESSED_PREFIX):
+ self.testnet = True
+ else:
+ self.testnet = False
+
+ def __str__(self):
+ return self.wif
+
+
+[docs]class PublicKey():
+ """
+ The class for public key object.
+
+ :param key: one of this types allowed:
+
+ - private key is instance of ``PrivateKey`` class
+ - private key HEX encoded string
+ - private key 32 bytes string
+ - private key in WIF format
+ - public key in HEX encoded string
+ - public key [33/65] bytes string
+
+ In case no key specified with HEX or bytes string you have to provide flag for testnet
+ and compressed key. WIF format and ``PrivateKey`` instance already contain this flags.
+ For HEX or bytes public key only testnet flag has the meaning, comressed flag is determined
+ according to the length of key.
+
+ :param compressed: (optional) if set to True private key corresponding compressed public key,
+ by default set to True. Recommended use only compressed public key.
+ :param testnet: (optional) if set to True mean that this private key for testnet Bitcoin network.
+
+ """
+ def __init__(self, key, compressed=True, testnet=False):
+ if isinstance(key, str):
+ try:
+ key = unhexlify(key)
+ except:
+ if is_wif_valid(key):
+ key = PrivateKey(key)
+
+ if isinstance(key, bytes):
+ if len(key) == 32:
+ key = PrivateKey(key, compressed=compressed, testnet=testnet)
+ elif is_public_key_valid(key):
+ public_key = key
+ self.testnet = testnet
+ self.compressed = True if len(key) == 33 else False
+ else:
+ raise TypeError("key invalid")
+
+ if isinstance(key, PrivateKey):
+ #: flag for testnet network private key (boolean)
+ self.testnet = key.testnet
+ #: flag for compressed type of corresponding public key (boolean)
+ self.compressed = key.compressed
+ public_key = private_to_public_key(key.key,
+ compressed=key.compressed,
+ hex=False)
+ #: public key in bytes (bytes)
+ self.key = public_key
+ #: public key in HEX (string)
+ self.hex = hexlify(self.key).decode()
+
+ def __str__(self):
+ return self.hex
+
+
+[docs]class Address():
+ """
+ The class for Address object.
+
+ :param key: (optional) one of this types allowed:
+
+ - private key WIF format
+ - instance of ``PrivateKey``
+ - private key HEX encoded string
+ - instance of ``PublicKey``
+
+ In case no key specified new Address will be created with random keys.
+ :param address_type: (optional) P2PKH, PUBKEY, P2WPKH, P2SH_P2WPKH, by default P2WPKH.
+ :param compressed: (optional) if set to True private key corresponding compressed public key,
+ by default set to True. Recommended use only compressed public key.
+ :param testnet: (optional) if set to True mean that this private key for testnet Bitcoin network.
+
+ In case instanse is created from WIF private key, ``PrivateKey`` or ``PublicKey`` compressed and testnet flags
+ already contain in initial key parameter and will be ignored.
+ """
+ def __init__(self, key=None,
+ address_type="P2WPKH", testnet=False, compressed=True):
+ if key is None:
+ #: instance of ``PrivateKey`` class
+ self.private_key = PrivateKey(testnet=testnet,
+ compressed=compressed)
+ #: instance of ``PublicKey`` class
+ self.public_key = PublicKey(self.private_key)
+ #: flag for testnet network address (boolean)
+ self.testnet = testnet
+ if isinstance(key, str) or isinstance(key, bytes):
+ key = PrivateKey(key, testnet=testnet, compressed=compressed)
+ if isinstance(key, PrivateKey):
+ self.private_key = key
+ self.testnet = key.testnet
+ compressed = key.compressed
+ self.public_key = PublicKey(self.private_key)
+ elif isinstance(key, PublicKey):
+ self.public_key = key
+ self.testnet = testnet
+ compressed = key.compressed
+ if address_type not in ("P2PKH", "PUBKEY", "P2WPKH", "P2SH_P2WPKH"):
+ raise TypeError("address type invalid")
+ if not compressed:
+ if address_type not in ("P2PKH", "PUBKEY", "P2SH"):
+ raise TypeError("compressed public key invalid")
+ #: flag for testnet network address (boolean)
+ self.type = address_type
+
+ if address_type in ("P2WPKH"):
+ #: version of witness program for SEGWIT address (string)
+ self.witness_version = 0
+ else:
+ self.witness_version = None
+ self.compressed = compressed
+ if address_type == "P2SH_P2WPKH":
+ #: flag for script hash address (boolean)
+ self.script_hash = True
+ #: redeeem script, only for P2SH_P2WPKH (bytes)
+ self.redeem_script = public_key_to_p2sh_p2wpkh_script(self.public_key.key)
+ #: redeeem script HEX, only for P2SH_P2WPKH (string)
+ self.redeem_script_hex = hexlify(self.redeem_script).decode()
+ #: address hash
+ self.hash = hash160(self.redeem_script)
+ self.witness_version = None
+ else:
+ self.script_hash = False
+ self.hash = hash160(self.public_key.key)
+ #: address hash HEX (string)
+ self.hash_hex = hexlify(self.hash).decode()
+ #: address in base58 or bech32 encoding (string)
+ self.address = hash_to_address(self.hash,
+ script_hash=self.script_hash,
+ witness_version=self.witness_version,
+ testnet=self.testnet)
+
+ def __str__(self):
+ return self.address
+
+
+class ScriptAddress():
+ def __init__(self, script, address_type="P2SH",
+ testnet=False, witness_version=None):
+ self.witness_version = witness_version
+ self.testnet = testnet
+ if isinstance(script, str):
+ script = unhexlify(script)
+ self.script = script
+ self.script_hex = hexlify(self.script).decode()
+ self.hash = hash160(self.script)
+ self.script_opcodes = decode_script(self.script)
+ self.script_opcodes_asm = decode_script(self.script, 1)
+ self.address = hash_to_address(self.hash,
+ script_hash=True,
+ witness_version=self.witness_version,
+ testnet=self.testnet)
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_modules/pybtc/block.html b/docs/build/html/_modules/pybtc/block.html
new file mode 100644
index 0000000..0c995c0
--- /dev/null
+++ b/docs/build/html/_modules/pybtc/block.html
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+from .tools import *
+from .transaction import Transaction
+from struct import pack, unpack
+
+
+[docs]class Block(dict):
+ def __init__(self, block):
+ s = get_stream(block)
+ self["header"] = s.read(80)
+ self["hash"] = double_sha256(self["header"])
+ self["version"] = unpack("<L", s.read(4))
+ self["previousBlockHash"] = s.read(32)
+ self["merkleRoot"] = s.read(32)
+ self["time"] = unpack("<L", s.read(4))
+ self["bits"] = s.read(4),
+ self["nonce"] = unpack("<L", s.read(4))
+ s.seek(-80, 1)
+ # self["tx"] = {i: Transaction(s)
+ # for i in range(var_int_to_int(read_var_int(s)))}
+ self["weight"] = 0
+ self["size"] = 0
+ self["strippedSize"] = 0
+ self["height"] = 0
+ self["difficulty"] = 0
+ self["targetDifficulty"] = 0
+ self["target"] = 0
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_modules/pybtc/tools.html b/docs/build/html/_modules/pybtc/tools.html
new file mode 100644
index 0000000..a04bb64
--- /dev/null
+++ b/docs/build/html/_modules/pybtc/tools.html
@@ -0,0 +1,1423 @@
+
+
+
+
+
+
+
+
+import time
+import struct
+from secp256k1 import ffi
+from .constants import *
+from .opcodes import *
+from .hash import *
+from .encode import *
+import math
+import io
+
+
+# Key management
+
+[docs]def create_private_key(compressed=True, testnet=False, wif=True, hex=False):
+ """
+ Create private key
+
+ :param compressed: (optional) Type of public key, by default set to compressed.
+ Using uncompressed public keys is deprecated in new SEGWIT addresses,
+ use this option only for backward compatibility.
+ :param testnet: (optional) flag for testnet network, by default is False.
+ :param wif: (optional) If set to True return key in WIF format, by default is True.
+ :param hex: (optional) If set to True return key in HEX format, by default is False.
+ :return: Private key in wif format (default), hex encoded byte string in case of hex flag or
+ raw bytes string in case wif and hex flags set to False.
+
+ """
+ a = random.SystemRandom().randint(0, MAX_INT_PRIVATE_KEY)
+ i = int((time.time() % 0.01)*100000)
+ h = a.to_bytes(32, byteorder="big")
+ # more entropy from system timer and sha256 derivation
+ while i:
+ h = hashlib.sha256(h).digest()
+ i -= 1
+ if not i and int.from_bytes(h, byteorder="big") > MAX_INT_PRIVATE_KEY:
+ i += 1
+ if wif:
+ return private_key_to_wif(h, compressed=compressed, testnet=testnet)
+ elif hex:
+ return hexlify(h).decode()
+ return h
+
+
+[docs]def private_key_to_wif(h, compressed=True, testnet=False):
+ """
+ Encode private key in HEX or RAW bytes format to WIF format.
+
+ :param h: private key 32 byte string or HEX encoded string.
+ :param compressed: (optional) flag of public key compressed format, by default set to True.
+ :param testnet: (optional) flag for testnet network, by default is False.
+ :return: Private key in WIF format.
+ """
+ # uncompressed: 0x80 + [32-byte secret] + [4 bytes of Hash() of previous 33 bytes], base58 encoded.
+ # compressed: 0x80 + [32-byte secret] + 0x01 + [4 bytes of Hash() previous 34 bytes], base58 encoded.
+ if isinstance(h, str):
+ h = unhexlify(h)
+ if len(h) != 32 and isinstance(h, bytes):
+ raise TypeError("private key must be a 32 bytes or hex encoded string")
+ if testnet:
+ h = TESTNET_PRIVATE_KEY_BYTE_PREFIX + h
+ else:
+ h = MAINNET_PRIVATE_KEY_BYTE_PREFIX + h
+ if compressed:
+ h += b'\x01'
+ h += double_sha256(h)[:4]
+ return encode_base58(h)
+
+
+[docs]def wif_to_private_key(h, hex=True):
+ """
+ Decode WIF private key to bytes string or HEX encoded string
+
+ :param hex: (optional) if set to True return key in HEX format, by default is True.
+ :return: Private key HEX encoded string or raw bytes string.
+ """
+ if not is_wif_valid(h):
+ raise TypeError("invalid wif key")
+ h = decode_base58(h)
+ if hex:
+ return hexlify(h[1:33]).decode()
+ return h[1:33]
+
+
+[docs]def is_wif_valid(wif):
+ """
+ Check is private key in WIF format string is valid.
+
+ :param wif: private key in WIF format string.
+ :return: boolean.
+ """
+ if not isinstance(wif, str):
+ raise TypeError("invalid wif key")
+ if wif[0] not in PRIVATE_KEY_PREFIX_LIST:
+ return False
+ try:
+ h = decode_base58(wif)
+ except:
+ return False
+ checksum = h[-4:]
+ if wif[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
+ TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
+ if len(h) != 37:
+ return False
+ elif len(h) != 38:
+ return False
+ if double_sha256(h[:-4])[:4] != checksum:
+ return False
+ return True
+
+
+[docs]def private_to_public_key(private_key, compressed=True, hex=True):
+ """
+ Get public key from private key using ECDSA secp256k1
+
+ :param private_key: private key in WIF, HEX or bytes.
+ :param compressed: (optional) flag of public key compressed format, by default set to True.
+ In case private_key in WIF format, this flag is set in accordance with
+ the key format specified in WIF string.
+ :param hex: (optional) if set to True return key in HEX format, by default is True.
+ :return: 33/65 bytes public key in HEX or bytes string.
+ """
+ if not isinstance(private_key, bytes):
+ if isinstance(private_key, bytearray):
+ private_key = bytes(private_key)
+ elif isinstance(private_key, str):
+ if not is_wif_valid(private_key):
+ private_key = unhexlify(private_key)
+ else:
+ if private_key[0] in (MAINNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX,
+ TESTNET_PRIVATE_KEY_UNCOMPRESSED_PREFIX):
+ compressed = False
+ private_key = wif_to_private_key(private_key, hex=0)
+ else:
+ raise TypeError("private key must be a bytes or WIF or hex encoded string")
+ pubkey_ptr = ffi.new('secp256k1_pubkey *')
+ r = secp256k1.secp256k1_ec_pubkey_create(ECDSA_CONTEXT_ALL, pubkey_ptr, private_key)
+ if not r:
+ raise RuntimeError("secp256k1 error")
+ len_key = 33 if compressed else 65
+ pubkey = ffi.new('char [%d]' % len_key)
+ outlen = ffi.new('size_t *', len_key)
+ compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED
+ r = secp256k1.secp256k1_ec_pubkey_serialize(ECDSA_CONTEXT_VERIFY, pubkey, outlen, pubkey_ptr, compflag)
+ pub = bytes(ffi.buffer(pubkey, len_key))
+ if not r:
+ raise RuntimeError("secp256k1 error")
+ return hexlify(pub).decode() if hex else pub
+
+
+[docs]def is_public_key_valid(key):
+ """
+ Check public key is valid.
+
+ :param key: public key in HEX or bytes string format.
+ :return: boolean.
+ """
+ if isinstance(key, str):
+ key = unhexlify(key)
+ if len(key) < 33:
+ return False
+ if key[0] == 0x04 and len(key) != 65:
+ return False
+ elif key[0] == 0x02 or key[0] == 0x03:
+ if len(key) != 33:
+ return False
+ return True
+
+
+# Addresses
+
+[docs]def hash_to_address(address_hash, testnet=False, script_hash=False, witness_version=0):
+ """
+ Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
+ P2WSH script hash is SHA256.
+
+
+ :param address_hash: public key hash or script hash in HEX or bytes string format.
+ :param testnet: (optional) flag for testnet network, by default is False.
+ :param script_hash: (optional) flag for script hash (P2SH address), by default is False.
+ :param witness_version: (optional) witness program version, by default is 0, for legacy
+ address format use None.
+ :return: address in base58 or bech32 format.
+ """
+ if isinstance(address_hash, str):
+ address_hash = unhexlify(address_hash)
+ if not isinstance(address_hash, bytes):
+ raise TypeError("address hash must be HEX encoded string or bytes")
+
+ if not script_hash:
+ if witness_version is None:
+ if len(address_hash) != 20:
+ raise TypeError("address hash length incorrect")
+ if testnet:
+ prefix = TESTNET_ADDRESS_BYTE_PREFIX
+ else:
+ prefix = MAINNET_ADDRESS_BYTE_PREFIX
+ address_hash = prefix + address_hash
+ address_hash += double_sha256(address_hash)[:4]
+ return encode_base58(address_hash)
+ else:
+ if len(address_hash) not in (20, 32):
+ raise TypeError("address hash length incorrect")
+
+ if witness_version is None:
+ if testnet:
+ prefix = TESTNET_SCRIPT_ADDRESS_BYTE_PREFIX
+ else:
+ prefix = MAINNET_SCRIPT_ADDRESS_BYTE_PREFIX
+ address_hash = prefix + address_hash
+ address_hash += double_sha256(address_hash)[:4]
+ return encode_base58(address_hash)
+
+ if testnet:
+ prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
+ hrp = TESTNET_SEGWIT_ADDRESS_PREFIX
+ else:
+ prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
+ hrp = MAINNET_SEGWIT_ADDRESS_PREFIX
+
+ address_hash = witness_version.to_bytes(1, "big") + rebase_8_to_5(address_hash)
+ checksum = bech32_polymod(prefix + address_hash + b"\x00" * 6)
+ checksum = rebase_8_to_5(checksum.to_bytes(5, "big"))[2:]
+ return "%s1%s" % (hrp, rebase_5_to_32(address_hash + checksum).decode())
+
+
+[docs]def public_key_to_address(pubkey, testnet=False, p2sh_p2wpkh=False, witness_version=0):
+ """
+ Get address from public key/script hash. In case PUBKEY, P2PKH, P2PKH public key/script hash is SHA256+RIPEMD160,
+ P2WSH script hash is SHA256.
+
+ :param pubkey: public key HEX or bytes string format.
+ :param testnet: (optional) flag for testnet network, by default is False.
+ :param p2sh_p2wpkh: (optional) flag for P2WPKH inside P2SH address, by default is False.
+ :param witness_version: (optional) witness program version, by default is 0, for legacy
+ address format use None.
+ :return: address in base58 or bech32 format.
+ """
+ if isinstance(pubkey, str):
+ pubkey = unhexlify(pubkey)
+ if not isinstance(pubkey, bytes):
+ raise TypeError("public key invalid")
+ if p2sh_p2wpkh:
+ if len(pubkey) != 33:
+ raise TypeError("public key invalid")
+ h = hash160(b'\x00\x14' + hash160(pubkey))
+ witness_version = None
+ else:
+ if witness_version is not None:
+ if len(pubkey) != 33:
+ raise TypeError("public key invalid")
+ h = hash160(pubkey)
+ return hash_to_address(h, testnet=testnet,
+ script_hash=p2sh_p2wpkh,
+ witness_version=witness_version)
+
+
+[docs]def address_to_hash(address, hex=True):
+ """
+ Get address hash from base58 or bech32 address format.
+
+ :param address: address in base58 or bech32 format.
+ :param hex: (optional) If set to True return key in HEX format, by default is True.
+ :return: script in HEX or bytes string.
+ """
+ if address[0] in ADDRESS_PREFIX_LIST:
+ h = decode_base58(address)[1:-4]
+ elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX,
+ TESTNET_SEGWIT_ADDRESS_PREFIX):
+ address = address.split("1")[1]
+ h = rebase_5_to_8(rebase_32_to_5(address)[1:-6], False)
+ else:
+ return None
+ return h.hex() if hex else h
+
+
+[docs]def address_type(address, num=False):
+ """
+ Get address type.
+
+ :param address: address in base58 or bech32 format.
+ :param num: (optional) If set to True return type in numeric format, by default is False.
+ :return: address type in string or numeric format.
+ """
+ if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
+ MAINNET_SCRIPT_ADDRESS_PREFIX):
+ t = 'P2SH'
+ elif address[0] in (MAINNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX_2):
+ t = 'P2PKH'
+ elif address[:2] in (MAINNET_SEGWIT_ADDRESS_PREFIX,
+ TESTNET_SEGWIT_ADDRESS_PREFIX):
+ if len(address) == 42:
+ t = 'P2WPKH'
+ elif len(address) == 62:
+ t = 'P2WSH'
+ else:
+ return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN'
+ else:
+ return SCRIPT_TYPES['NON_STANDARD'] if num else 'UNKNOWN'
+ return SCRIPT_TYPES[t] if num else t
+
+
+def address_net_type(address):
+ """
+ Get address network type.
+
+ :param address: address in base58 or bech32 format.
+ :return: address network type in string format or None.
+ """
+ if address[0] in (MAINNET_SCRIPT_ADDRESS_PREFIX,
+ MAINNET_ADDRESS_PREFIX):
+ return "mainnet"
+ elif address[:2] == MAINNET_SEGWIT_ADDRESS_PREFIX:
+ return "mainnet"
+ elif address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX_2):
+ return "testnet"
+ elif address[:2] == TESTNET_SEGWIT_ADDRESS_PREFIX:
+ return "testnet"
+ return None
+
+
+[docs]def address_to_script(address, hex=False):
+ """
+ Get public key script from address.
+
+ :param address: address in base58 or bech32 format.
+ :param hex: (optional) If set to True return key in HEX format, by default is True.
+ :return: public key script in HEX or bytes string.
+ """
+ if address[0] in (TESTNET_SCRIPT_ADDRESS_PREFIX,
+ MAINNET_SCRIPT_ADDRESS_PREFIX):
+ s = [BYTE_OPCODE["OP_HASH160"],
+ b'\x14',
+ address_to_hash(address, hex=False),
+ BYTE_OPCODE["OP_EQUAL"]]
+ elif address[0] in (MAINNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX_2):
+ s = [BYTE_OPCODE["OP_DUP"],
+ BYTE_OPCODE["OP_HASH160"],
+ b'\x14',
+ address_to_hash(address, hex=False),
+ BYTE_OPCODE["OP_EQUALVERIFY"],
+ BYTE_OPCODE["OP_CHECKSIG"]]
+ elif address[:2] in (TESTNET_SEGWIT_ADDRESS_PREFIX,
+ MAINNET_SEGWIT_ADDRESS_PREFIX):
+ h = address_to_hash(address, hex=False)
+ s = [BYTE_OPCODE["OP_0"],
+ bytes([len(h)]),
+ h]
+ else:
+ raise TypeError("address invalid")
+ s = b''.join(s)
+ return hexlify(s).decode() if hex else s
+
+
+def public_key_to_p2sh_p2wpkh_script(pubkey):
+ if len(pubkey) != 33:
+ raise TypeError("public key len invalid")
+ return b'\x00\x14%s' % hash160(pubkey)
+
+
+[docs]def is_address_valid(address, testnet=False):
+ """
+ Check is address valid.
+
+ :param address: address in base58 or bech32 format.
+ :param testnet: (optional) flag for testnet network, by default is False.
+ :return: boolean.
+ """
+ if not address or type(address) != str:
+ return False
+ if address[0] in (MAINNET_ADDRESS_PREFIX,
+ MAINNET_SCRIPT_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX_2,
+ TESTNET_SCRIPT_ADDRESS_PREFIX):
+ if testnet:
+ if address[0] not in (TESTNET_ADDRESS_PREFIX,
+ TESTNET_ADDRESS_PREFIX_2,
+ TESTNET_SCRIPT_ADDRESS_PREFIX):
+ return False
+ else:
+ if address[0] not in (MAINNET_ADDRESS_PREFIX,
+ MAINNET_SCRIPT_ADDRESS_PREFIX):
+ return False
+ h = decode_base58(address)
+ if len(h) != 25:
+ return False
+ checksum = h[-4:]
+ if double_sha256(h[:-4])[:4] != checksum:
+ return False
+ return True
+ elif address[:2].lower() in (TESTNET_SEGWIT_ADDRESS_PREFIX,
+ MAINNET_SEGWIT_ADDRESS_PREFIX):
+ if len(address) not in (42, 62):
+ return False
+ try:
+ prefix, payload = address.split('1')
+ except:
+ return False
+ upp = True if prefix[0].isupper() else False
+ for i in payload[1:]:
+ if upp:
+ if not i.isupper() or i not in base32charset_upcase:
+ return False
+ else:
+ if i.isupper() or i not in base32charset:
+ return False
+ payload = payload.lower()
+ prefix = prefix.lower()
+ if testnet:
+ if prefix != TESTNET_SEGWIT_ADDRESS_PREFIX:
+ return False
+ stripped_prefix = TESTNET_SEGWIT_ADDRESS_BYTE_PREFIX
+ else:
+ if prefix != MAINNET_SEGWIT_ADDRESS_PREFIX:
+ return False
+ stripped_prefix = MAINNET_SEGWIT_ADDRESS_BYTE_PREFIX
+ d = rebase_32_to_5(payload)
+ address_hash = d[:-6]
+ checksum = d[-6:]
+ checksum2 = bech32_polymod(stripped_prefix + address_hash + b"\x00" * 6)
+ checksum2 = rebase_8_to_5(checksum2.to_bytes(5, "big"))[2:]
+ if checksum != checksum2:
+ return False
+ return True
+
+
+def get_witness_version(address):
+ address = address.split("1")[1]
+ h = rebase_32_to_5(address)
+ return h[0]
+
+
+# Script
+
+[docs]def parse_script(script, segwit=True):
+ """
+ Parse script and return script type, script address and required signatures count.
+
+ :param script: script in bytes string or HEX encoded string format.
+ :param segwit: (optional) If set to True recognize P2WPKH and P2WSH sripts, by default set to True.
+
+ :return: dictionary:
+
+ - nType - numeric script type
+ - type - script type
+ - addressHash - address hash in case address recognized
+ - script - script if no address recognized
+ - reqSigs - required signatures count
+ """
+ if not script:
+ return {"nType": 7, "type": "NON_STANDARD", "reqSigs": 0, "script": b""}
+ if type(script) == str:
+ try:
+ script = unhexlify(script)
+ except:
+ pass
+ assert type(script) == bytes
+ l = len(script)
+ if segwit:
+ if l == 22 and script[0] == 0:
+ return {"nType": 5, "type": "P2WPKH", "reqSigs": 1, "addressHash": script[2:]}
+ if l == 34 and script[0] == 0:
+ return {"nType": 6, "type": "P2WSH", "reqSigs": None, "addressHash": script[2:]}
+ if l == 25 and \
+ script[:2] == b"\x76\xa9" and \
+ script[-2:] == b"\x88\xac":
+ return {"nType": 0, "type": "P2PKH", "reqSigs": 1, "addressHash": script[3:-2]}
+ if l == 23 and \
+ script[0] == 169 and \
+ script[-1] == 135:
+ return {"nType": 1, "type": "P2SH", "reqSigs": None, "addressHash": script[2:-1]}
+ if l == 67 and script[-1] == 172:
+ return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])}
+ if l == 35 and script[-1] == 172:
+ return {"nType": 2, "type": "PUBKEY", "reqSigs": 1, "addressHash": hash160(script[1:-1])}
+ if script[0] == 106 and l > 1 and l <= 82:
+ if script[1] == l - 2:
+ return {"nType": 3, "type": "NULL_DATA", "reqSigs": 0, "data": script[2:]}
+ if script[0] >= 81 and script[0] <= 96:
+ if script[-1] == 174:
+ if script[-2] >= 81 and script[-2] <= 96:
+ if script[-2] >= script[0]:
+ c, s = 0, 1
+ while l - 2 - s > 0:
+ if script[s] < 0x4c:
+ s += script[s]
+ c += 1
+ else:
+ c = 0
+ break
+ s += 1
+ if c == script[-2] - 80:
+ return {"nType": 4, "type": "MULTISIG", "reqSigs": script[0] - 80, "script": script}
+
+ s, m, n, last, req_sigs = 0, 0, 0, 0, 0
+ while l - s > 0:
+ if script[s] >= 81 and script[s] <= 96:
+ if not n:
+ n = script[s] - 80
+ else:
+ if m == 0:
+ n, m = script[s] - 80, 0
+ elif n > m:
+ n, m = script[s] - 80, 0
+ elif m == script[s] - 80:
+ last = 0 if last else 2
+ elif script[s] < 0x4c:
+ s += script[s]
+ m += 1
+ if m > 16:
+ n, m = 0, 0
+ elif script[s] == OPCODE["OP_PUSHDATA1"]:
+ try:
+ s += 1 + script[s + 1]
+ except:
+ break
+ elif script[s] == OPCODE["OP_PUSHDATA2"]:
+ try:
+ s += 2 + struct.unpack('<H', script[s: s + 2])[0]
+ except:
+ break
+ elif script[s] == OPCODE["OP_PUSHDATA4"]:
+ try:
+ s += 4 + struct.unpack('<L', script[s: s + 4])[0]
+ except:
+ break
+ else:
+ if script[s] == OPCODE["OP_CHECKSIG"]:
+ req_sigs += 1
+ elif script[s] == OPCODE["OP_CHECKSIGVERIFY"]:
+ req_sigs += 1
+ elif script[s] in (OPCODE["OP_CHECKMULTISIG"], OPCODE["OP_CHECKMULTISIGVERIFY"]):
+ if last:
+ req_sigs += n
+ else:
+ req_sigs += 20
+ n, m = 0, 0
+ if last:
+ last -= 1
+ s += 1
+ return {"nType": 7, "type": "NON_STANDARD", "reqSigs": req_sigs, "script": script}
+
+
+[docs]def decode_script(script, asm=False):
+ """
+ Decode script to ASM format or to human readable OPCODES string.
+
+ :param script: script in bytes string or HEX encoded string format.
+ :param asm: (optional) If set to True decode to ASM fromat, by default set to False.
+ :return: script in ASM format string or OPCODES string.
+ """
+ if isinstance(script, str):
+ try:
+ script = unhexlify(script)
+ except:
+ pass
+ if not isinstance(script, bytes):
+ raise TypeError("script invalid")
+ l = len(script)
+ s = 0
+ result = []
+ while l - s > 0:
+ if script[s] < 0x4c and script[s]:
+ if asm:
+ result.append(hexlify(script[s + 1:s + 1 + script[s]]).decode())
+ else:
+ result.append('[%s]' % script[s])
+ s += script[s] + 1
+ continue
+ elif script[s] == OPCODE["OP_PUSHDATA1"]:
+ s += 1 + script[s + 1]
+ elif script[s] == OPCODE["OP_PUSHDATA2"]:
+ s += 2 + struct.unpack('<H', script[s: s + 2])
+ elif script[s] == OPCODE["OP_PUSHDATA4"]:
+ s += 4 + struct.unpack('<L', script[s: s + 4])
+ result.append(RAW_OPCODE[script[s]])
+ s += 1
+ return ' '.join(result)
+
+
+[docs]def delete_from_script(script, sub_script):
+ """
+ Decode OPCODE or subscript from script.
+
+ :param script: traget script in bytes or HEX encoded string.
+ :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string.
+ :return: script in bytes or HEX encoded string corresponding to the format of target script.
+ """
+ if not sub_script:
+ return script
+ s_hex = False
+ if isinstance(script, str):
+ try:
+ script = unhexlify(script)
+ s_hex = True
+ except:
+ pass
+ if isinstance(sub_script, str):
+ try:
+ sub_script = unhexlify(sub_script)
+ except:
+ pass
+
+ if not isinstance(script, bytes):
+ raise TypeError("script invalid")
+ if not isinstance(sub_script, bytes):
+ raise TypeError("sub_script invalid")
+
+ l = len(script)
+ ls = len(sub_script)
+ s = 0
+ k = 0
+ stack = []
+ result = []
+ while l - s > 0:
+ if script[s] < 0x4c and script[s]:
+ stack.append(script[s] + 1)
+ s += script[s] + 1
+ elif script[s] == OPCODE["OP_PUSHDATA1"]:
+ stack.append(1 + script[s + 1])
+ s += 1 + script[s + 1]
+ elif script[s] == OPCODE["OP_PUSHDATA2"]:
+ stack.append(2 + struct.unpack('<H', script[s: s + 2]))
+ s += 2 + struct.unpack('<H', script[s: s + 2])
+ elif script[s] == OPCODE["OP_PUSHDATA4"]:
+ stack.append(4 + struct.unpack('<L', script[s: s + 4]))
+ s += 4 + struct.unpack('<L', script[s: s + 4])
+ else:
+ stack.append(1)
+ s += 1
+ if s - k >= ls:
+ if script[k:s][:ls] == sub_script:
+ if s - k > ls:
+ result.append(script[k + ls:s])
+ t = 0
+ while t != s - k:
+ t += stack.pop(0)
+ k = s
+ else:
+ t = stack.pop(0)
+ result.append(script[k:k + t])
+ k += t
+ if script[k:s][:ls] == sub_script:
+ if s - k > ls:
+ result.append(script[k + ls:s])
+ else:
+ result.append(script[k:k + ls])
+
+ return b''.join(result) if not s_hex else hexlify(b''.join(result)).decode()
+
+
+[docs]def script_to_hash(script, witness=False, hex=True):
+ """
+ Encode script to hash HASH160 or SHA256 in dependency of the witness.
+
+ :param script: script in bytes or HEX encoded string.
+ :param witness: (optional) If set to True return SHA256 hash for P2WSH, by default is False.
+ :param hex: (optional) If set to True return key in HEX format, by default is True.
+ :param sub_script: sub_script which is necessary to remove from target script in bytes or HEX encoded string.
+ :return: script in bytes or HEX encoded string corresponding to the format of target script.
+ """
+ if isinstance(script, str):
+ s = unhexlify(script)
+ if witness:
+ return sha256(script, hex)
+ else:
+ return hash160(script, hex)
+
+
+# Signatures
+
+[docs]def verify_signature(sig, pub_key, msg):
+ """
+ Verify signature for message and given public key
+
+ :param sig: signature in bytes or HEX encoded string.
+ :param pub_key: public key in bytes or HEX encoded string.
+ :param msg: message in bytes or HEX encoded string.
+ :return: boolean.
+ """
+ if not isinstance(sig, bytes):
+ if isinstance(sig, bytearray):
+ sig = bytes(sig)
+ elif isinstance(sig, str):
+ sig = unhexlify(sig)
+ else:
+ raise TypeError("signature must be a bytes or hex encoded string")
+ if not isinstance(pub_key, bytes):
+ if isinstance(pub_key, bytearray):
+ pub_key = bytes(pub_key)
+ elif isinstance(pub_key, str):
+ pub_key = unhexlify(pub_key)
+ else:
+ raise TypeError("public key must be a bytes or hex encoded string")
+ if not isinstance(msg, bytes):
+ if isinstance(msg, bytearray):
+ msg = bytes(msg)
+ elif isinstance(msg, str):
+ msg = unhexlify(msg)
+ else:
+ raise TypeError("message must be a bytes or hex encoded string")
+
+ raw_sig = ffi.new('secp256k1_ecdsa_signature *')
+ raw_pubkey = ffi.new('secp256k1_pubkey *')
+ if not secp256k1.secp256k1_ecdsa_signature_parse_der(ECDSA_CONTEXT_VERIFY, raw_sig, sig, len(sig)):
+ raise TypeError("signature must be DER encoded")
+ if not secp256k1.secp256k1_ec_pubkey_parse(ECDSA_CONTEXT_VERIFY, raw_pubkey, pub_key, len(pub_key)):
+ raise TypeError("public key format error")
+ result = secp256k1.secp256k1_ecdsa_verify(ECDSA_CONTEXT_VERIFY, raw_sig, msg, raw_pubkey)
+ return True if result else False
+
+
+[docs]def sign_message(msg, private_key, hex=True):
+ """
+ Sign message
+
+ :param msg: message to sign bytes or HEX encoded string.
+ :param private_key: private key (bytes, hex encoded string or WIF format)
+ :param hex: (optional) If set to True return key in HEX format, by default is True.
+ :return: DER encoded signature in bytes or HEX encoded string.
+ """
+ if isinstance(msg, bytearray):
+ msg = bytes(msg)
+ if isinstance(msg, str):
+ try:
+ msg = unhexlify(msg)
+ except:
+ pass
+ if not isinstance(msg, bytes):
+ raise TypeError("message must be a bytes or hex encoded string")
+
+ if isinstance(private_key, bytearray):
+ private_key = bytes(private_key)
+ if isinstance(private_key, str):
+ try:
+ private_key = unhexlify(private_key)
+ except:
+ if is_wif_valid(private_key):
+ private_key = wif_to_private_key(private_key, hex=False)
+ if not isinstance(private_key, bytes):
+ raise TypeError("private key must be a bytes, hex encoded string or in WIF format")
+
+ raw_sig = ffi.new('secp256k1_ecdsa_signature *')
+ signed = secp256k1.secp256k1_ecdsa_sign(ECDSA_CONTEXT_SIGN, raw_sig, msg,
+ private_key, ffi.NULL, ffi.NULL)
+ if not signed:
+ raise RuntimeError("secp256k1 error")
+ len_sig = 74
+ output = ffi.new('unsigned char[%d]' % len_sig)
+ outputlen = ffi.new('size_t *', len_sig)
+ res = secp256k1.secp256k1_ecdsa_signature_serialize_der(ECDSA_CONTEXT_SIGN,
+ output, outputlen, raw_sig)
+ if not res:
+ raise RuntimeError("secp256k1 error")
+ signature = bytes(ffi.buffer(output, outputlen[0]))
+ return hexlify(signature).decode() if hex else signature
+
+
+[docs]def is_valid_signature_encoding(sig):
+ """
+ Check is valid signature encoded in DER format
+
+ :param sig: signature in bytes or HEX encoded string.
+ :return: boolean.
+ """
+ # Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
+ # * total-length: 1-byte length descriptor of everything that follows,
+ # excluding the sighash byte.
+ # * R-length: 1-byte length descriptor of the R value that follows.
+ # * R: arbitrary-length big-endian encoded R value. It must use the shortest
+ # possible encoding for a positive integers (which means no null bytes at
+ # the start, except a single one when the next byte has its highest bit set).
+ # * S-length: 1-byte length descriptor of the S value that follows.
+ # * S: arbitrary-length big-endian encoded S value. The same rules apply.
+ # * sighash: 1-byte value indicating what data is hashed (not part of the DER
+ # signature)
+ length = len(sig)
+ # Minimum and maximum size constraints.
+ if (length < 9) or (length > 73):
+ return False
+ # A signature is of type 0x30 (compound).
+ if sig[0] != 0x30:
+ return False
+ # Make sure the length covers the entire signature.
+ if sig[1] != (length - 3):
+ return False
+ # Extract the length of the R element.
+ len_r = sig[3]
+ # Make sure the length of the S element is still inside the signature.
+ if (5 + len_r) >= length:
+ return False
+ # Extract the length of the S element.
+ len_s = sig[5 + len_r]
+ # Verify that the length of the signature matches the sum of the length
+ # of the elements.
+ if (len_r + len_s + 7) != length:
+ return False
+ # Check whether the R element is an integer.
+ if sig[2] != 0x02:
+ return False
+ # Zero-length integers are not allowed for R.
+ if len_r == 0:
+ return False
+ # Negative numbers are not allowed for R.
+ if sig[4] & 0x80:
+ return False
+ # Null bytes at the start of R are not allowed, unless R would
+ # otherwise be interpreted as a negative number.
+ if (len_r > 1) and (sig[4] == 0x00) and (not sig[5] & 0x80):
+ return False
+ # Check whether the S element is an integer.
+ if sig[len_r + 4] != 0x02:
+ return False
+ # Zero-length integers are not allowed for S.
+ if len_s == 0:
+ return False
+ # Negative numbers are not allowed for S.
+ if sig[len_r + 6] & 0x80:
+ return False
+ # Null bytes at the start of S are not allowed, unless S would otherwise be
+ # interpreted as a negative number.
+ if (len_s > 1) and (sig[len_r + 6] == 0x00) and (not sig[len_r + 7] & 0x80):
+ return False
+ return True
+
+
+# Hash encoding
+
+[docs]def rh2s(raw_hash):
+ """
+ Encode raw transaction hash to HEX string with bytes order change
+
+ :param raw_hash: transaction hash in bytes string.
+ :return: HEX encoded string.
+ """
+ return hexlify(raw_hash[::-1]).decode()
+
+
+[docs]def s2rh(hash_string):
+ """
+ Decode HEX transaction hash to bytes with byte order change
+
+ :param raw_hash: transaction hash in bytes string.
+ :return: bytes string.
+ """
+ return unhexlify(hash_string)[::-1]
+
+
+def s2rh_step4(hash_string):
+ h = unhexlify(hash_string)
+ return reverse_hash(h)
+
+
+[docs]def reverse_hash(raw_hash):
+ """
+ Reverse hash order
+
+ :param raw_hash: bytes string.
+ :return: bytes string.
+ """
+ return struct.pack('>IIIIIIII', *struct.unpack('>IIIIIIII', raw_hash)[::-1])[::-1]
+
+
+# Merkle root
+
+[docs]def merkle_root(tx_hash_list, hex=True):
+ """
+ Calculate merkle root from transaction hash list
+
+ :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
+ :param hex: (optional) If set to True return result in HEX format, by default is True.
+ :return: merkle root in bytes or HEX encoded string corresponding hex flag.
+ """
+ tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list]
+ if len(tx_hash_list) == 1:
+ return tx_hash_list[0]
+ while True:
+ new_hash_list = list()
+ while tx_hash_list:
+ h1 = tx_hash_list.pop(0)
+ try:
+ h2 = tx_hash_list.pop(0)
+ except:
+ h2 = h1
+ new_hash_list.append(double_sha256(h1 + h2))
+ if len(new_hash_list) > 1:
+ tx_hash_list = new_hash_list
+ else:
+ return new_hash_list[0] if not hex else hexlify(new_hash_list[0]).decode()
+
+
+[docs]def merkle_branches(tx_hash_list, hex=True):
+ """
+ Calculate merkle branches for coinbase transacton
+
+ :param tx_hash_list: list of transaction hashes in bytes or HEX encoded string.
+ :param hex: (optional) If set to True return result in HEX format, by default is True.
+ :return: list of merkle branches in bytes or HEX encoded string corresponding hex flag.
+ """
+ tx_hash_list = [h if isinstance(h, bytes) else s2rh(h) for h in tx_hash_list]
+ branches = []
+ if len(tx_hash_list) == 1:
+ return []
+ tx_hash_list.pop(0)
+ while True:
+ branches.append(tx_hash_list.pop(0))
+ new_hash_list = list()
+ while tx_hash_list:
+ h1 = tx_hash_list.pop(0)
+ try:
+ h2 = tx_hash_list.pop(0)
+ except:
+ h2 = h1
+ new_hash_list.append(double_sha256(h1 + h2))
+ if len(new_hash_list) > 1:
+ tx_hash_list = new_hash_list
+ else:
+ if new_hash_list:
+ branches.append(new_hash_list.pop(0))
+ return branches if not hex else [hexlify(h).decode() for h in branches]
+
+
+[docs]def merkleroot_from_branches(merkle_branches, coinbase_hash, hex=True):
+ """
+ Calculate merkle root from merkle branches and coinbase transacton hash
+
+ :param merkle_branches: list merkle branches in bytes or HEX encoded string.
+ :param coinbase_hash: list coinbase transaction hash in bytes or HEX encoded string.
+ :param hex: (optional) If set to True return result in HEX format, by default is True.
+ :return: merkle root in bytes or HEX encoded string corresponding hex flag.
+ """
+ merkle_root = coinbase_hash if not isinstance(coinbase_hash, str) else unhexlify(coinbase_hash)
+ for h in merkle_branches:
+ if type(h) == str:
+ h = unhexlify(h)
+ merkle_root = double_sha256(merkle_root + h)
+ return merkle_root if not hex else hexlify(merkle_root).decode()
+
+
+# Difficulty
+
+
+[docs]def bits_to_target(bits):
+ """
+ Calculate target from bits
+
+ :param bits: HEX string, bytes string or integer representation of bits.
+ :return: integer.
+ """
+ if type(bits) == str:
+ bits = unhexlify(bits)
+ if type(bits) == bytes:
+ return int.from_bytes(bits[1:], 'big') * (2 ** (8 * (bits[0] - 3)))
+ else:
+ shift = bits >> 24
+ target = (bits & 0xffffff) * (1 << (8 * (shift - 3)))
+ return target
+
+
+[docs]def target_to_difficulty(target):
+ """
+ Calculate difficulty from target
+
+ :param target: integer.
+ :return: float.
+ """
+ return 0x00000000FFFF0000000000000000000000000000000000000000000000000000 / target
+
+
+[docs]def bits_to_difficulty(bits):
+ """
+ Calculate difficulty from bits
+
+ :param bits: HEX string, bytes string or integer representation of bits.
+ :return: integer.
+ """
+ return target_to_difficulty(bits_to_target(bits))
+
+
+[docs]def difficulty_to_target(difficulty):
+ """
+ Calculate target from difficulty
+
+ :param target: integer.
+ :return: float.
+ """
+ return int(0x00000000FFFF0000000000000000000000000000000000000000000000000000 / difficulty)
+
+
+# Tools
+
+
+[docs]def bytes_needed(n):
+ """
+ Calculate bytes needed to convert integer to bytes.
+
+ :param n: integer.
+ :return: integer.
+ """
+ if n == 0:
+ return 1
+ return math.ceil(n.bit_length()/8)
+
+
+[docs]def int_to_bytes(i, byteorder='big'):
+ """
+ Convert integer to bytes.
+
+ :param n: integer.
+ :param byteorder: (optional) byte order 'big' or 'little', by default 'big'.
+ :return: bytes.
+ """
+ return i.to_bytes(bytes_needed(i), byteorder=byteorder, signed=False)
+
+
+[docs]def bytes_to_int(i, byteorder='big'):
+ """
+ Convert bytes to integer.
+
+ :param i: bytes.
+ :param byteorder: (optional) byte order 'big' or 'little', by default 'big'.
+ :return: integer.
+ """
+ return int.from_bytes(i, byteorder=byteorder, signed=False)
+
+
+# variable integer
+
+[docs]def int_to_var_int(i):
+ """
+ Convert integer to variable integer
+
+ :param i: integer.
+ :return: bytes.
+ """
+ if i < 0xfd:
+ return struct.pack('<B', i)
+ if i <= 0xffff:
+ return b'\xfd' + struct.pack('<H', i)
+ if i <= 0xffffffff:
+ return b'\xfe' + struct.pack('<L', i)
+ return b'\xff' + struct.pack('<Q', i)
+
+
+[docs]def var_int_to_int(data):
+ """
+ Convert variable integer to integer
+
+ :param data: bytes vriable integer.
+ :return: integer.
+ """
+ if data[0] == 0xfd:
+ return struct.unpack('<H', data[1:3])[0]
+ elif data[0] == 0xfe:
+ return struct.unpack('<L', data[1:5])[0]
+ elif data[0] == 0xff:
+ return struct.unpack('<Q', data[1:9])[0]
+ return data[0]
+
+
+[docs]def var_int_len(n):
+ """
+ Get variable integer length in bytes from integer value
+
+ :param n: integer.
+ :return: integer.
+ """
+ if n <= 0xfc:
+ return 1
+ if n <= 0xffff:
+ return 3
+ elif n <= 0xffffffff:
+ return 5
+ return 9
+
+
+[docs]def get_var_int_len(bytes):
+ """
+ Get variable integer length in bytes from bytes
+
+ :param bytes: bytes.
+ :return: integer.
+ """
+ if bytes[0] == 253:
+ return 3
+ elif bytes[0] == 254:
+ return 5
+ elif bytes[0] == 255:
+ return 9
+ return 1
+
+
+[docs]def read_var_int(stream):
+ """
+ Read variable integer from io.BytesIO stream to bytes
+
+ :param stream: io.BytesIO stream.
+ :return: bytes.
+ """
+ l = stream.read(1)
+ bytes_length = get_var_int_len(l)
+ return l + stream.read(bytes_length - 1)
+
+
+[docs]def read_var_list(stream, data_type):
+ """
+ Read variable integer list from io.BytesIO stream to bytes
+
+ :param stream: io.BytesIO stream.
+ :param data_type: list data type.
+ :return: list of data_type.
+ """
+ count = var_int_to_int(read_var_int(stream))
+ return [data_type.deserialize(stream) for i in range(count)]
+
+# compressed integer
+
+
+[docs]def int_to_c_int(n, base_bytes=1):
+ """
+ Convert integer to compresed integer
+
+ :param n: integer.
+ :param base_bytes: len of bytes base from which start compression.
+ :return: bytes.
+ """
+ if n == 0:
+ return b'\x00'
+ else:
+ l = n.bit_length() + 1
+ min_bits = base_bytes * 8 - 1
+ if l <= min_bits + 1:
+ return n.to_bytes(base_bytes, byteorder="big")
+ prefix = 0
+ payload_bytes = math.ceil((l)/8) - base_bytes
+ extra_bytes = int(math.ceil((l+payload_bytes)/8) - base_bytes)
+ for i in range(extra_bytes):
+ prefix += 2 ** i
+ if l < base_bytes * 8:
+ l = base_bytes * 8
+ prefix = prefix << l
+ if prefix.bit_length() % 8:
+ prefix = prefix << 8 - prefix.bit_length() % 8
+ n ^= prefix
+ return n.to_bytes(math.ceil(n.bit_length()/8), byteorder="big")
+
+
+[docs]def c_int_to_int(b, base_bytes=1):
+ """
+ Convert compressed integer bytes to integer
+
+ :param b: compressed integer bytes.
+ :param base_bytes: len of bytes base from which start compression.
+ :return: integer.
+ """
+ byte_length = 0
+ f = 0
+ while True:
+ v = b[f]
+ if v == 0xff:
+ byte_length += 8
+ f += 1
+ continue
+ while v & 0b10000000:
+ byte_length += 1
+ v = v << 1
+ break
+ n = int.from_bytes(b[:byte_length+base_bytes], byteorder="big")
+ if byte_length:
+ return n & ((1 << (byte_length+base_bytes) * 8 - byte_length) - 1)
+ return n
+
+
+[docs]def c_int_len(n, base_bytes=1):
+ """
+ Get length of compressed integer from integer value
+
+ :param n: bytes.
+ :param base_bytes: len of bytes base from which start compression.
+ :return: integer.
+ """
+ if n == 0:
+ return 1
+ l = n.bit_length() + 1
+ min_bits = base_bytes * 8 - 1
+ if l <= min_bits + 1:
+ return 1
+ payload_bytes = math.ceil((l)/8) - base_bytes
+ return int(math.ceil((l+payload_bytes)/8))
+
+
+# generic big endian MPI format
+def bn_bytes(v, have_ext=False):
+ ext = 0
+ if have_ext:
+ ext = 1
+ return ((v.bit_length() + 7) // 8) + ext
+
+
+def bn2bin(v):
+ s = bytearray()
+ i = bn_bytes(v)
+ while i > 0:
+ s.append((v >> ((i - 1) * 8)) & 0xff)
+ i -= 1
+ return s
+
+
+def bin2bn(s):
+ l = 0
+ for ch in s:
+ l = (l << 8) | ch
+ return l
+
+
+def bn2mpi(v):
+ have_ext = False
+ if v.bit_length() > 0:
+ have_ext = (v.bit_length() & 0x07) == 0
+ neg = False
+ if v < 0:
+ neg = True
+ v = -v
+ s = struct.pack(b">I", bn_bytes(v, have_ext))
+ ext = bytearray()
+ if have_ext:
+ ext.append(0)
+ v_bin = bn2bin(v)
+ if neg:
+ if have_ext:
+ ext[0] |= 0x80
+ else:
+ v_bin[0] |= 0x80
+ return s + ext + v_bin
+
+
+def mpi2bn(s):
+ if len(s) < 4:
+ return None
+ s_size = bytes(s[:4])
+ v_len = struct.unpack(b">I", s_size)[0]
+ if len(s) != (v_len + 4):
+ return None
+ if v_len == 0:
+ return 0
+ v_str = bytearray(s[4:])
+ neg = False
+ i = v_str[0]
+ if i & 0x80:
+ neg = True
+ i &= ~0x80
+ v_str[0] = i
+ v = bin2bn(v_str)
+
+ if neg:
+ return -v
+ return v
+
+# bitcoin-specific little endian format, with implicit size
+
+
+def mpi2vch(s):
+ r = s[4:] # strip size
+ # if r:
+ r = r[::-1] # reverse string, converting BE->LE
+ # else: r=b'\x00'
+ return r
+
+
+def bn2vch(v):
+ return bytes(mpi2vch(bn2mpi(v)))
+
+
+def vch2mpi(s):
+ r = struct.pack(b">I", len(s)) # size
+ r += s[::-1] # reverse string, converting LE->BE
+ return r
+
+
+def vch2bn(s):
+ return mpi2bn(vch2mpi(s))
+
+
+def i2b(i): return bn2vch(i)
+
+
+def b2i(b): return vch2bn(b)
+
+
+def get_stream(stream):
+ if type(stream) != io.BytesIO:
+ if type(stream) == str:
+ stream = unhexlify(stream)
+ if type(stream) == bytes:
+ stream = io.BytesIO(stream)
+ else:
+ raise TypeError
+ return stream
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_modules/pybtc/transaction.html b/docs/build/html/_modules/pybtc/transaction.html
new file mode 100644
index 0000000..1da5271
--- /dev/null
+++ b/docs/build/html/_modules/pybtc/transaction.html
@@ -0,0 +1,767 @@
+
+
+
+
+
+
+
+
+
+from struct import unpack
+import json
+from .tools import *
+from .address import PrivateKey, Address, PublicKey, ScriptAddress
+from binascii import hexlify, unhexlify
+
+
+[docs]class Transaction(dict):
+ def __init__(self, raw_tx=None, tx_format="decoded", version=1, lockTime=0, testnet=False):
+ assert tx_format in ("decoded", "raw")
+ self["format"] = tx_format
+ self["testnet"] = testnet
+ self["segwit"] = False
+ self["txId"] = None
+ self["hash"] = None
+ self["version"] = version
+ self["size"] = 0
+ self["vSize"] = 0
+ self["bSize"] = 0
+ self["lockTime"] = lockTime
+ self["vIn"] = dict()
+ self["vOut"] = dict()
+ self["rawTx"] = None
+ self["blockHash"] = None
+ self["confirmations"] = None
+ self["time"] = None
+ self["blockTime"] = None
+ self["blockIndex"] = None
+ self["coinbase"] = False
+ self["fee"] = None
+ self["data"] = None
+ self["amount"] = None
+ if raw_tx is None:
+ return
+ self["amount"] = 0
+ stream = self.get_stream(raw_tx)
+ start = stream.tell()
+ (self["version"],) = unpack('<L', stream.read(4))
+ n = read_var_int(stream)
+ sw = 0
+ sw_len = 0
+ if n == b'\x00':
+ sw = 1
+ self["flag"] = stream.read(1)
+ n = read_var_int(stream)
+ ic = var_int_to_int(n)
+ for k in range(ic):
+ self["vIn"][k] = dict()
+ self["vIn"][k]["txId"] = stream.read(32)
+ self["vIn"][k]["vOut"] = unpack('<L', stream.read(4))[0]
+ n = var_int_to_int(read_var_int(stream))
+ self["vIn"][k]["scriptSig"] = stream.read(n)
+ (self["vIn"][k]["sequence"],) = unpack('<L', stream.read(4))
+ for k in range(var_int_to_int(read_var_int(stream))):
+ self["vOut"][k] = dict()
+ self["vOut"][k]["value"] = unpack('<Q', stream.read(8))[0]
+ self["amount"] += self["vOut"][k]["value"]
+ self["vOut"][k]["scriptPubKey"] = stream.read(var_int_to_int(read_var_int(stream)))
+ s = parse_script(self["vOut"][k]["scriptPubKey"], sw)
+ self["vOut"][k]["nType"] = s["nType"]
+ self["vOut"][k]["type"] = s["type"]
+ if self["data"] is None:
+ if s["nType"] == 3:
+ self["data"] = s["data"]
+ if s["nType"] not in (3, 4, 7):
+ self["vOut"][k]["addressHash"] = s["addressHash"]
+ self["vOut"][k]["reqSigs"] = s["reqSigs"]
+ if sw:
+ sw = stream.tell() - start
+ for k in range(ic):
+ self["vIn"][k]["txInWitness"] = [stream.read(var_int_to_int(read_var_int(stream))) \
+ for c in range(var_int_to_int(read_var_int(stream)))]
+ sw_len = stream.tell() - sw + 2
+ self["lockTime"] = unpack('<L', stream.read(4))[0]
+ end = stream.tell()
+ stream.seek(start)
+ b = stream.read(end - start)
+ self["rawTx"] = b
+ self["size"] = end - start
+ self["bSize"] = end - start - sw_len
+ self["weight"] = self["bSize"] * 3 + self["size"]
+ self["vSize"] = math.ceil(self["weight"] / 4)
+ if ic == 1 and \
+ self["vIn"][0]["txId"] == b'\x00' * 32 and \
+ self["vIn"][0]["vOut"] == 0xffffffff:
+ self["coinbase"] = True
+ else:
+ self["coinbase"] = False
+ if sw:
+ self["segwit"] = True
+ self["hash"] = double_sha256(b)
+ self["txId"] = double_sha256(b[:4] + b[6:sw] + b[-4:])
+ else:
+ self["segwit"] = False
+ self["txId"] = double_sha256(b)
+ self["hash"] = self["txId"]
+
+ def decode(self, testnet=None):
+ if self["format"] == "decoded":
+ self.encode()
+ self["format"] = "decoded"
+ if testnet is not None:
+ self["testnet"] = testnet
+ if type(self["txId"]) == bytes:
+ self["txId"] = rh2s(self["txId"])
+ if "flag" in self:
+ if type(self["flag"]) == bytes:
+ self["flag"] = rh2s(self["flag"])
+ if type(self["hash"]) == bytes:
+ self["hash"] = rh2s(self["hash"])
+ if type(self["rawTx"]) == bytes:
+ self["rawTx"] = hexlify(self["rawTx"]).decode()
+ for i in self["vIn"]:
+ if type(self["vIn"][i]["txId"]) == bytes:
+ self["vIn"][i]["txId"] = rh2s(self["vIn"][i]["txId"])
+ if type(self["vIn"][i]["scriptSig"]) == bytes:
+ self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode()
+ try:
+ t = list()
+ for w in self["vIn"][i]["txInWitness"]:
+ if type(w) == bytes:
+ w = hexlify(w).decode()
+ t.append(w)
+ self["vIn"][i]["txInWitness"] = t
+ self["vIn"][i]["txInWitnessAsm"] = [decode_script(ws, 1) for ws in
+ self["vIn"][i]["txInWitness"]]
+ self["vIn"][i]["txInWitnessOpcodes"] = [decode_script(ws) for ws in
+ self["vIn"][i]["txInWitness"]]
+ except:
+ pass
+ try:
+ if type(self["vIn"][i]["addressHash"]) == bytes:
+ self["vIn"][i]["addressHash"] = hexlify(self["vIn"][i]["addressHash"]).decode()
+ sh = True if self["vIn"][i]["nType"] in (1, 5) else False
+ witness_version = None if self["vIn"][i]["nType"] < 5 else 0
+ self["vIn"][i]["address"] = hash_to_address(self["vIn"][i]["addressHash"],
+ self["testnet"],
+ sh,
+ witness_version)
+ except:
+ pass
+ if "scriptPubKey" in self["vIn"][i]:
+ if type(self["vIn"][i]["scriptPubKey"]) == bytes:
+ self["vIn"][i]["scriptPubKey"] = hexlify(self["vIn"][i]["scriptPubKey"]).decode()
+ self["vIn"][i]["scriptPubKeyOpcodes"] = decode_script(self["vIn"][i]["scriptPubKey"])
+ self["vIn"][i]["scriptPubKeyAsm"] = decode_script(self["vIn"][i]["scriptPubKey"], 1)
+ if "redeemScript" in self["vIn"][i]:
+ if type(self["vIn"][i]["redeemScript"]) == bytes:
+ self["vIn"][i]["redeemScript"] = hexlify(self["vIn"][i]["redeemScript"]).decode()
+ self["vIn"][i]["redeemScriptOpcodes"] = decode_script(self["vIn"][i]["redeemScript"])
+ self["vIn"][i]["redeemScriptAsm"] = decode_script(self["vIn"][i]["redeemScript"], 1)
+ if not self["coinbase"]:
+ if type(self["vIn"][i]["scriptSig"]) == bytes:
+ self["vIn"][i]["scriptSig"] = hexlify(self["vIn"][i]["scriptSig"]).decode()
+ self["vIn"][i]["scriptSigOpcodes"] = decode_script(self["vIn"][i]["scriptSig"])
+ self["vIn"][i]["scriptSigAsm"] = decode_script(self["vIn"][i]["scriptSig"], 1)
+
+ for i in self["vOut"]:
+ if type(self["vOut"][i]["scriptPubKey"]) == bytes:
+ self["vOut"][i]["scriptPubKey"] = hexlify(self["vOut"][i]["scriptPubKey"]).decode()
+ try:
+ if type(self["vOut"][i]["addressHash"]) == bytes:
+ self["vOut"][i]["addressHash"] = hexlify(self["vOut"][i]["addressHash"]).decode()
+ sh = True if self["vOut"][i]["nType"] in (1, 5) else False
+ witness_version = None if self["vOut"][i]["nType"] < 5 else 0
+ self["vOut"][i]["address"] = hash_to_address(self["vOut"][i]["addressHash"],
+ self["testnet"],
+ sh,
+ witness_version)
+ except:
+ pass
+ self["vOut"][i]["scriptPubKeyOpcodes"] = decode_script(self["vOut"][i]["scriptPubKey"])
+ self["vOut"][i]["scriptPubKeyAsm"] = decode_script(self["vOut"][i]["scriptPubKey"], 1)
+ if "data" in self:
+ if type(self["data"]) == bytes:
+ self["data"] = hexlify(self["data"]).decode()
+ return self
+
+ def encode(self):
+ if type(self["txId"]) == str:
+ self["txId"] = s2rh(self["txId"])
+ if "flag" in self:
+ if type(self["flag"]) == str:
+ self["flag"] = s2rh(self["flag"])
+ if type(self["hash"]) == str:
+ self["hash"] = s2rh(self["hash"])
+ if type(self["rawTx"]) == str:
+ self["rawTx"] = unhexlify(self["rawTx"])
+
+ for i in self["vIn"]:
+ if type(self["vIn"][i]["txId"]) == str:
+ self["vIn"][i]["txId"] = s2rh(self["vIn"][i]["txId"])
+ if type(self["vIn"][i]["scriptSig"]) == str:
+ self["vIn"][i]["scriptSig"] = unhexlify(self["vIn"][i]["scriptSig"])
+ try:
+ t = list()
+ for w in self["vIn"][i]["txInWitness"]:
+ if type(w) == str:
+ w = unhexlify(w)
+ t.append(w)
+ self["vIn"][i]["txInWitness"] = t
+ if "txInWitnessAsm" in self["vIn"][i]:
+ del self["vIn"][i]["txInWitnessAsm"]
+ if "txInWitnessOpcodes" in self["vIn"][i]:
+ del self["vIn"][i]["txInWitnessOpcodes"]
+ except:
+ pass
+ try:
+ if type(self["vIn"][i]["addressHash"]) == str:
+ self["vIn"][i]["addressHash"] = unhexlify(self["vIn"][i]["addressHash"])
+ if "address" in self["vIn"][i]:
+ del self["vIn"][i]["address"]
+ except:
+ pass
+ if "scriptSigAsm" in self["vIn"][i]:
+ del self["vIn"][i]["scriptSigAsm"]
+ if "scriptSigOpcodes" in self["vIn"][i]:
+ del self["vIn"][i]["scriptSigOpcodes"]
+
+ for i in self["vOut"]:
+ if type(self["vOut"][i]["scriptPubKey"]) == str:
+ self["vOut"][i]["scriptPubKey"] = unhexlify(self["vOut"][i]["scriptPubKey"])
+ try:
+ if type(self["vOut"][i]["addressHash"]) == str:
+ self["vOut"][i]["addressHash"] = unhexlify(self["vOut"][i]["addressHash"])
+ if "address" in self["vOut"][i]:
+ del self["vOut"][i]["address"]
+ except:
+ pass
+ if "scriptPubKeyOpcodes" in self["vOut"][i]:
+ del self["vOut"][i]["scriptPubKeyOpcodes"]
+ if "scriptPubKeyAsm" in self["vOut"][i]:
+ del self["vOut"][i]["scriptPubKeyAsm"]
+
+ if "data" in self:
+ if type(self["data"]) == str:
+ self["data"] = unhexlify(self["data"])
+ self["format"] = "raw"
+ return self
+
+ def get_stream(self, stream):
+ if type(stream) != io.BytesIO:
+ if type(stream) == str:
+ stream = unhexlify(stream)
+ if type(stream) == bytes:
+ stream = io.BytesIO(stream)
+ else:
+ raise TypeError
+ return stream
+
+ def serialize(self, segwit=True, hex=True):
+ chunks = []
+ chunks.append(struct.pack('<L', self["version"]))
+ if segwit and self["segwit"]:
+ chunks.append(b"\x00\x01")
+ chunks.append(int_to_var_int(len(self["vIn"])))
+ for i in self["vIn"]:
+ if type(self["vIn"][i]['txId']) == bytes:
+ chunks.append(self["vIn"][i]['txId'])
+ else:
+ chunks.append(s2rh(self["vIn"][i]['txId']))
+ chunks.append(struct.pack('<L', self["vIn"][i]['vOut']))
+ if type(self["vIn"][i]['scriptSig']) == bytes:
+ chunks.append(int_to_var_int(len(self["vIn"][i]['scriptSig'])))
+ chunks.append(self["vIn"][i]['scriptSig'])
+ else:
+ chunks.append(int_to_var_int(int(len(self["vIn"][i]['scriptSig']) / 2)))
+ chunks.append(unhexlify(self["vIn"][i]['scriptSig']))
+ chunks.append(struct.pack('<L', self["vIn"][i]['sequence']))
+ chunks.append(int_to_var_int(len(self["vOut"])))
+ for i in self["vOut"]:
+ chunks.append(struct.pack('<Q', self["vOut"][i]['value']))
+ if type(self["vOut"][i]['scriptPubKey']) == bytes:
+ chunks.append(int_to_var_int(len(self["vOut"][i]['scriptPubKey'])))
+ chunks.append(self["vOut"][i]['scriptPubKey'])
+ else:
+ chunks.append(int_to_var_int(int(len(self["vOut"][i]['scriptPubKey']) / 2)))
+ chunks.append(unhexlify(self["vOut"][i]['scriptPubKey']))
+ if segwit and self["segwit"]:
+ for i in self["vIn"]:
+ chunks.append(int_to_var_int(len(self["vIn"][i]['txInWitness'])))
+ for w in self["vIn"][i]['txInWitness']:
+ if type(w) == bytes:
+ chunks.append(int_to_var_int(len(w)))
+ chunks.append(w)
+ else:
+ chunks.append(int_to_var_int(int(len(w) / 2)))
+ chunks.append(unhexlify(w))
+ chunks.append(struct.pack('<L', self['lockTime']))
+ tx = b''.join(chunks)
+ return tx if not hex else hexlify(tx).decode()
+
+ def json(self):
+ try:
+ return json.dumps(self)
+ except:
+ pass
+ return json.dumps(self.decode())
+
+ def add_input(self, tx_id=None, v_out=0, sequence=0xffffffff,
+ script_sig=b"", tx_in_witness=None, amount=None,
+ script_pub_key=None, address=None, private_key=None):
+ if tx_id is None:
+ tx_id = b"\x00" * 32
+ v_out = 0xffffffff
+ assert v_out == 0xffffffff and sequence == 0xffffffff
+ assert not self["vIn"]
+ if type(tx_id) == str:
+ tx_id = s2rh(tx_id)
+ if type(script_sig) == str:
+ script_sig = unhexlify(script_sig)
+ assert type(tx_id) == bytes
+ assert len(tx_id) == 32
+ assert type(v_out) == int
+ assert v_out <= 0xffffffff and v_out >= 0
+ assert type(sequence) == int
+ assert sequence <= 0xffffffff and sequence >= 0
+ assert type(script_sig) == bytes
+ assert len(script_sig) <= 520
+ if private_key:
+ if type(private_key) != PrivateKey:
+ private_key = PrivateKey(private_key)
+ if amount:
+ assert type(amount) == int
+ assert amount >= 0 and amount <= MAX_AMOUNT
+ if tx_in_witness:
+ assert type(tx_in_witness) == list
+ l = 0
+ witness = []
+ for w in tx_in_witness:
+ if type(w) == str:
+ witness.append(unhexlify(w) if self["format"] == "raw" else w)
+ else:
+ witness.append(w if self["format"] == "raw" else unhexlify(w))
+ l += 1 + len(w)
+ if len(w) >= 0x4c:
+ l += 1
+ if len(w) > 0xff:
+ l += 1
+ # witness script limit
+ assert l <= 10000
+ if tx_id == b"\x00" * 32:
+ assert v_out == 0xffffffff and sequence == 0xffffffff and len(script_sig) <= 100
+ self["coinbase"] = True
+
+ # script_pub_key
+ if script_pub_key:
+ if type(script_pub_key) == str:
+ script_pub_key = unhexlify(script_pub_key)
+ type(script_pub_key) == bytes
+ if address is not None:
+ if type(address) == str:
+ net = True if address_net_type(address) == 'mainnet' else False
+ assert not net == self["testnet"]
+ script = address_to_script(address)
+ elif type(address) in (Address, ScriptAddress):
+ assert type(address) == Address
+ script = address_to_script(address.address)
+ if script_pub_key:
+ assert script_pub_key == script
+ else:
+ script_pub_key = script
+
+ k = len(self["vIn"])
+ self["vIn"][k] = dict()
+ self["vIn"][k]["vOut"] = v_out
+ self["vIn"][k]["sequence"] = sequence
+ if self["format"] == "raw":
+ self["vIn"][k]["txId"] = tx_id
+ self["vIn"][k]["scriptSig"] = script_sig
+ if script_pub_key:
+ self["vIn"][k]["scriptPubKey"] = script_pub_key
+ else:
+ self["vIn"][k]["txId"] = rh2s(tx_id)
+ self["vIn"][k]["scriptSig"] = hexlify(script_sig).decode()
+ self["vIn"][k]["scriptSigOpcodes"] = decode_script(script_sig)
+ self["vIn"][k]["scriptSigAsm"] = decode_script(script_sig, 1)
+ if script_pub_key:
+ self["vIn"][k]["scriptPubKey"] = hexlify(script_pub_key).decode()
+ if tx_in_witness:
+ self["segwit"] = True
+ self["vIn"][k]["txInWitness"] = witness
+ if amount:
+ self["vIn"][k]["value"] = amount
+ if private_key:
+ self["vIn"][k].private_key = private_key
+ self.__refresh__()
+ return self
+
+ def add_output(self, amount, address=None, script_pub_key=None):
+ assert address is not None or script_pub_key is not None
+ assert not (address is None and script_pub_key is None)
+ assert type(amount) == int
+ assert amount >= 0 and amount <= MAX_AMOUNT
+ if script_pub_key:
+ if type(script_pub_key) == str:
+ script_pub_key = unhexlify(script_pub_key)
+ assert type(script_pub_key) == bytes
+ else:
+ if type(address) == Address:
+ address = address.address
+ script_pub_key = address_to_script(address)
+
+ k = len(self["vOut"])
+ self["vOut"][k] = dict()
+ self["vOut"][k]["value"] = amount
+ segwit = True if "segwit" in self else False
+ s = parse_script(script_pub_key, segwit)
+ self["vOut"][k]["nType"] = s["nType"]
+ self["vOut"][k]["type"] = s["type"]
+
+ if self["format"] == "raw":
+ self["vOut"][k]["scriptPubKey"] = script_pub_key
+ if self["data"] is None:
+ if s["nType"] == 3:
+ self["data"] = s["data"]
+ if s["nType"] not in (3, 4, 7):
+ self["vOut"][k]["addressHash"] = s["addressHash"]
+ self["vOut"][k]["reqSigs"] = s["reqSigs"]
+ else:
+ self["vOut"][k]["scriptPubKey"] = hexlify(script_pub_key).decode()
+ if self["data"] is None:
+ if s["nType"] == 3:
+ self["data"] = hexlify(s["data"]).decode()
+ if s["nType"] not in (3, 4, 7):
+ self["vOut"][k]["addressHash"] = hexlify(s["addressHash"]).decode()
+ self["vOut"][k]["reqSigs"] = s["reqSigs"]
+ self["vOut"][k]["scriptPubKeyOpcodes"] = decode_script(script_pub_key)
+ self["vOut"][k]["scriptPubKeyAsm"] = decode_script(script_pub_key, 1)
+ sh = True if self["vOut"][k]["nType"] in (1, 5) else False
+ witness_version = None if self["vOut"][k]["nType"] < 5 else 0
+ if "addressHash" in self["vOut"][k]:
+ self["vOut"][k]["address"] = hash_to_address(self["vOut"][k]["addressHash"],
+ self["testnet"],
+ sh,
+ witness_version)
+ self.__refresh__()
+ return self
+
+ def del_output(self, n=None):
+ if not self["vOut"]:
+ return self
+ if n is None:
+ n = len(self["vOut"]) - 1
+ new_out = dict()
+ c = 0
+ for i in range(len(self["vOut"])):
+ if i != n:
+ new_out[c] = self["vOut"][i]
+ c += 1
+ self["vOut"] = new_out
+ self.__refresh__()
+ return self
+
+ def del_input(self, n):
+ if not self["vIn"]:
+ return self
+ if n is None:
+ n = len(self["vIn"]) - 1
+ new_in = dict()
+ c = 0
+ for i in range(len(self["vIn"])):
+ if i != n:
+ new_in[c] = self["vIn"][i]
+ c += 1
+ self["vIn"] = new_in
+ self.__refresh__()
+ return self
+
+ def sign_input(self, n, private_key=None, script_pub_key=None, redeem_script=None, sighash_type=SIGHASH_ALL):
+ if private_key is not None:
+ if private_key:
+ if type(private_key) != PrivateKey:
+ private_key_obj = PrivateKey(private_key)
+ public_key = PublicKey(private_key_obj).key
+ private_key = private_key_obj.key
+ else:
+ if "privateKey" not in self["vIn"][n]:
+ return self
+ private_key = self["vIn"][n].private_key.key
+ public_key = PublicKey(self["vIn"][n].private_key).key
+
+ if redeem_script:
+ if type(redeem_script) == str:
+ redeem_script = unhexlify(redeem_script).decode()
+ assert type(redeem_script) == bytes
+ script = redeem_script
+ else:
+ script = script_pub_key
+
+ sighash = self.sig_hash_input(n, script_pub_key=script, sighash_type=sighash_type)
+ if type(sighash) == str:
+ sighash = s2rh(sighash)
+ signature = sign_message(sighash, private_key, 0) + bytes([sighash_type])
+ if redeem_script:
+ if self["vIn"][n]["scriptSig"]:
+ sig_script = self["vIn"][n]["scriptSig"]
+ if type(sig_script) == str:
+ sig_script = unhexlify(sig_script).decode()
+ sig_script = bytes([len(public_key)]) + public_key + sig_script
+ sig_script = bytes([len(signature)]) + signature + sig_script
+ else:
+ sig_script = bytes([len(signature)]) + signature
+ sig_script += bytes([len(public_key)]) + public_key
+ if len(redeem_script) <= 0x4b:
+ sig_script += bytes([len(redeem_script)]) + redeem_script
+ elif len(redeem_script) <= 0xff:
+ sig_script = BYTE_OPCODE["OP_PUSHDATA1"] + bytes([len(redeem_script)]) + redeem_script
+ elif len(redeem_script) <= 0xffff:
+ sig_script = BYTE_OPCODE["OP_PUSHDATA2"] + bytes([len(redeem_script)]) + redeem_script
+ else:
+ sig_script = BYTE_OPCODE["OP_PUSHDATA4"] + bytes([len(redeem_script)]) + redeem_script
+ else:
+ sig_script = bytes([len(signature)]) + signature
+ sig_script += bytes([len(public_key)]) + public_key
+ if self["format"] == "raw":
+ self["vIn"][n]["scriptSig"] = sig_script
+ else:
+ self["vIn"][n]["scriptSig"] = hexlify(sig_script).decode()
+ self["vIn"][n]["scriptSigOpcodes"] = decode_script(sig_script)
+ self["vIn"][n]["scriptSigAsm"] = decode_script(sig_script, 1)
+ self.__refresh__()
+ return self
+
+ def sig_hash_input(self, n, script_pub_key=None, sighash_type=SIGHASH_ALL):
+ # check n
+ assert n >= 0
+ tx_in_count = len(self["vIn"])
+
+ if n >= tx_in_count:
+ if self["format"] == "raw":
+ return b'\x01' + b'\x00' * 31
+ else:
+ return rh2s(b'\x01' + b'\x00' * 31)
+
+ # check script_pub_key for input
+ if script_pub_key is not None:
+ script_code = script_pub_key
+ else:
+ assert "scriptPubKey" in self["vIn"][n]
+ script_code = self["vIn"][n]["scriptPubKey"]
+ if type(script_code) == str:
+ script_code = unhexlify(script_code)
+ assert type(script_code) == bytes
+
+ # remove opcode separators
+ script_code = delete_from_script(script_code, BYTE_OPCODE["OP_CODESEPARATOR"])
+ preimage = bytearray()
+
+ if ((sighash_type & 31) == SIGHASH_SINGLE) and (n >= (len(self["vOut"]))):
+ if self["format"] == "raw":
+ return b'\x01' + b'\x00' * 31
+ else:
+ return rh2s(b'\x01' + b'\x00' * 31)
+
+ preimage += struct.pack('<L', self["version"])
+ preimage += b'\x01' if sighash_type & SIGHASH_ANYONECANPAY else int_to_var_int(tx_in_count)
+
+ for i in self["vIn"]:
+ # skip all other inputs for SIGHASH_ANYONECANPAY case
+ if (sighash_type & SIGHASH_ANYONECANPAY) and (n != i):
+ continue
+ sequence = self["vIn"][i]["sequence"]
+ if (sighash_type & 31) == SIGHASH_SINGLE and (n != i):
+ sequence = 0
+ if (sighash_type & 31) == SIGHASH_NONE and (n != i):
+ sequence = 0
+ tx_id = self["vIn"][i]["txId"]
+ if type(tx_id) == str:
+ tx_id = s2rh(tx_id)
+ input = tx_id + struct.pack('<L', self["vIn"][i]["vOut"])
+ if n == i:
+ input += int_to_var_int(len(script_code)) + script_code
+ input += struct.pack('<L', sequence)
+ else:
+ input += b'\x00' + struct.pack('<L', sequence)
+ preimage += input
+
+ if (sighash_type & 31) == SIGHASH_NONE:
+ preimage += b'\x00'
+ else:
+ if (sighash_type & 31) == SIGHASH_SINGLE:
+ preimage += int_to_var_int(n + 1)
+ else:
+ preimage += int_to_var_int(len(self["vOut"]))
+
+ if (sighash_type & 31) != SIGHASH_NONE:
+ for i in self["vOut"]:
+ script_pub_key = self["vOut"][i]["scriptPubKey"]
+ if type(self["vOut"][i]["scriptPubKey"]) == str:
+ script_pub_key = unhexlify(script_pub_key)
+ if i > n and (sighash_type & 31) == SIGHASH_SINGLE:
+ continue
+ if (sighash_type & 31) == SIGHASH_SINGLE and (n != i):
+ preimage += b'\xff' * 8 + b'\x00'
+ else:
+ preimage += self["vOut"][i]["value"].to_bytes(8, 'little')
+ preimage += int_to_var_int(len(script_pub_key)) + script_pub_key
+
+ preimage += self["lockTime"].to_bytes(4, 'little')
+ preimage += struct.pack(b"<i", sighash_type)
+ return double_sha256(preimage) if self["format"] == "raw" else rh2s(double_sha256(preimage))
+
+ def __refresh__(self):
+ if not self["vOut"] or not self["vIn"]:
+ return
+ no_segwit_view = self.serialize(segwit=False, hex=False)
+ self["txId"] = double_sha256(no_segwit_view)
+ self["rawTx"] = self.serialize(segwit=True, hex=False)
+ self["hash"] = double_sha256(self["rawTx"])
+
+ self["size"] = len(self["rawTx"])
+ self["bSize"] = len(no_segwit_view)
+ self["weight"] = self["bSize"] * 3 + self["size"]
+ self["vSize"] = math.ceil(self["weight"] / 4)
+
+ if self["format"] != "raw":
+ self["txId"] = rh2s(self["txId"])
+ self["hash"] = rh2s(self["hash"])
+ self["rawTx"] = hexlify(self["rawTx"]).decode()
+
+ input_sum = 0
+ for i in self["vIn"]:
+ if "value" in self["vIn"][i]:
+ input_sum += self["vIn"][i]["value"]
+ else:
+ input_sum = None
+ break
+
+ output_sum = 0
+ for i in self["vOut"]:
+ if "value" in self["vOut"][i]:
+ output_sum += self["vOut"][i]["value"]
+ else:
+ output_sum = None
+ break
+ self["amount"] = output_sum
+ if output_sum and input_sum:
+ self["fee"] = input_sum - output_sum
+ else:
+ self["fee"] = None
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/build/html/_sources/address.rst.txt b/docs/build/html/_sources/address.rst.txt
new file mode 100644
index 0000000..fc02023
--- /dev/null
+++ b/docs/build/html/_sources/address.rst.txt
@@ -0,0 +1,30 @@
+=========
+Addresses
+=========
+
+Collection of base classes that implement the work with Bitcoin addresses and address keys.
+Supports addresses types PUBKEY, P2PKH, P2SH, P2SH-PWPKH, P2WPKH, P2WSH.
+
+|
+|
+
+.. autoclass:: pybtc.PrivateKey
+ :members:
+ :inherited-members:
+
+|
+|
+
+.. autoclass:: pybtc.PublicKey
+ :members:
+ :inherited-members:
+
+|
+|
+
+.. autoclass:: pybtc.Address
+ :members:
+ :inherited-members:
+
+
+
diff --git a/docs/build/html/_sources/block.rst.txt b/docs/build/html/_sources/block.rst.txt
new file mode 100644
index 0000000..027e75c
--- /dev/null
+++ b/docs/build/html/_sources/block.rst.txt
@@ -0,0 +1,10 @@
+======
+Blocks
+======
+
+The class for creating transaction.
+
+
+
+.. autoclass:: pybtc.Block
+
\ No newline at end of file
diff --git a/docs/build/html/_sources/classes.rst.txt b/docs/build/html/_sources/classes.rst.txt
new file mode 100644
index 0000000..4555cae
--- /dev/null
+++ b/docs/build/html/_sources/classes.rst.txt
@@ -0,0 +1,13 @@
+=========
+Reference
+=========
+
+.. toctree::
+ :name: mastertoc
+ :maxdepth: 2
+
+ address.rst
+ transaction.rst
+ block.rst
+
+
diff --git a/docs/build/html/_sources/contributing.rst.txt b/docs/build/html/_sources/contributing.rst.txt
new file mode 100644
index 0000000..a7b801b
--- /dev/null
+++ b/docs/build/html/_sources/contributing.rst.txt
@@ -0,0 +1,36 @@
+.. _pybtc-contributing:
+
+============
+Contributing
+============
+
+Instructions for contributors
+-----------------------------
+
+
+In order to make a clone of the GitHub repo: open the link and press the
+"Fork" button on the upper-right menu of the web page.
+
+
+Workflow is pretty straightforward:
+
+ 1. Clone the GitHub
+
+ 2. Make a change
+
+ 3. Make sure all tests passed
+
+ 4. Add a record intp file into ``change.log``.
+
+ 5. Commit changes to own aiohttp clone
+
+ 6. Make pull request from github page for your clone against master branch
+
+
+Tests coverage
+--------------
+
+We are trying hard to have good test coverage; please don't make it worse.
+All tests located in ``tests/`` folder.
+
+
diff --git a/docs/build/html/_sources/examples.rst.txt b/docs/build/html/_sources/examples.rst.txt
new file mode 100644
index 0000000..3dc5ae3
--- /dev/null
+++ b/docs/build/html/_sources/examples.rst.txt
@@ -0,0 +1,118 @@
+========
+Examples
+========
+
+
+Create address
+--------------
+
+This is example of usage Address class. The address class implements the work with addresses controlled by a private key.
+Supports the ability to create P2WPKH, P2PKH, PUBKEY address types and P2SH_P2WPKH as exception for SEGWIT adoption.
+It is recommended to use native SEGWIT address type - P2WPKH, which reduces costs of miner fee and expand block capacity.
+To create an address, you need to create a class object. Buy default,
+will be created P2WPKH address for mainnet.
+
+
+
+.. code-block:: bash
+
+ >>> import pybtc
+ >>> a = pybtc.Address()
+ >>> a.address
+ 'bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5s'
+ >>> a.private_key.wif
+ 'L5XKGA2xEHcinWEpmyiABS1bqQux8Av5dGVqcpRtVJC3ZCR5sXUe'
+ >>>
+ >>> # create P2PKH legacy format
+ >>> pybtc.Address(address_type="P2PKH").address
+ '1ChpKurzFhdCULKaNHCc3Ra9KfxM2LRguw'
+ >>>
+ >>> # create testnet address
+ >>> pybtc.Address(address_type="P2PKH", testnet=True).address
+ 'mpR4hDfu269yxgZtPVYSD21gtpvdxpTmH6'
+ >>>
+ >>> # create P2SH_P2WPKH SEGWIT adoption address
+ >>> pybtc.Address(address_type="P2SH_P2WPKH").address
+ '3Bqeq3XqL6azMK3BxNyr8vXgXUtoG63J4T'
+ >>>
+
+
+Get address from key
+--------------------
+
+In case you already have private or public key you can object from your key.
+
+.. code-block:: bash
+
+ >>> a = pybtc.Address('L5XKGA2xEHcinWEpmyiABS1bqQux8Av5dGVqcpRtVJC3ZCR5sXUe')
+ >>> a.address
+ 'bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5s'
+ >>> a.public_key.hex
+ '03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af'
+ >>>
+ >>> # get address from public key
+ >>> pub = pybtc.PublicKey('03b8b44876e1f45be7e42953ea47026c39cc45341344d3ab32701b93de696107af')
+ >>>
+ >>> pybtc.Address(pub).address
+ 'bc1q6cxx5t8xkruz3s5khx7923xvsx5ry4c6p74m5s'
+ >>>
+
+Pure functions for address
+--------------------------
+
+Create private key
+
+.. code-block:: bash
+
+ >>> import pybtc
+ >>> pybtc.create_private_key()
+ 'KyvZYvdzWD4JSPFt4wXwjG53as227zT2qiWbMTicZEUSjiwvbEqi'
+ >>>
+ >>> pybtc.create_private_key(compressed=False)
+ '5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr'
+ >>>
+ >>> pybtc.is_wif_valid('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr')
+ True
+ >>> pybtc.is_wif_valid('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381**********Jto9ZTnr')
+ False
+ >>>
+
+Get public key from private key
+
+.. code-block:: bash
+
+ >>> import pybtc
+ >>> pybtc.private_to_public_key('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr')
+ '0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f'
+ >>>
+ >>> pybtc.public_key_to_address('0479f17a94410afd4f27588a192bacada53add0741765092dc0f8e2a29ea1bcd276dbc1ef74c3e0172d9db8047f2a0a5dc2e8e51a13f7f0cc072de906b765e0f7f')
+ >>>
+ >>> # this is uncompressed public key, so we can't create witness address
+ >>> # we have to set witness_version to None to get non segwit address
+ >>> pub = pybtc.private_to_public_key('5Jw8DY1uBrd35xup6eD6KLEFa4AJFbX381HWuHvPGirJto9ZTnr')
+ >>> pybtc.public_key_to_address(pub, witness_version=None)
+ '17mXwxxZRmj1nJJzDszZbW9URSAradEuAt'
+ >>>
+
+Tools
+
+.. code-block:: bash
+
+ >>> pybtc.is_address_valid('17mXwxxZRmj1nJJzDszZbW9URSAradEuAt')
+ True
+ >>> pybtc.address_type('17mXwxxZRmj1nJJzDszZbW9URSAradEuAt')
+ 'P2PKH'
+ >>> pybtc.address_net_type('17mXwxxZRmj1nJJzDszZbW9URSAradEuAt')
+ 'mainnet'
+ >>>
+
+
+Create script address
+---------------------
+
+
+
+
+
+
+
diff --git a/docs/build/html/_sources/functional.rst.txt b/docs/build/html/_sources/functional.rst.txt
new file mode 100644
index 0000000..f0d3686
--- /dev/null
+++ b/docs/build/html/_sources/functional.rst.txt
@@ -0,0 +1,108 @@
+
+========================
+Pure functions reference
+========================
+
+Base function primitives implemented in functional programming paradigm.
+
+
+
+Private keys
+============
+
+.. autofunction:: pybtc.create_private_key
+.. autofunction:: pybtc.private_key_to_wif
+.. autofunction:: pybtc.wif_to_private_key
+.. autofunction:: pybtc.is_wif_valid
+
+
+Public keys
+===========
+
+.. WARNING::
+ Using uncompressed public keys is
+ `deprecated ' + _('Hide Search Matches') + '
') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); \ No newline at end of file diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js new file mode 100644 index 0000000..893cd39 --- /dev/null +++ b/docs/build/html/_static/documentation_options.js @@ -0,0 +1,9 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt' +}; \ No newline at end of file diff --git a/docs/build/html/_static/down-pressed.png b/docs/build/html/_static/down-pressed.png new file mode 100644 index 0000000..5756c8c Binary files /dev/null and b/docs/build/html/_static/down-pressed.png differ diff --git a/docs/build/html/_static/down.png b/docs/build/html/_static/down.png new file mode 100644 index 0000000..1b3bdad Binary files /dev/null and b/docs/build/html/_static/down.png differ diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/docs/build/html/_static/file.png differ diff --git a/docs/build/html/_static/jquery-3.2.1.js b/docs/build/html/_static/jquery-3.2.1.js new file mode 100644 index 0000000..d2d8ca4 --- /dev/null +++ b/docs/build/html/_static/jquery-3.2.1.js @@ -0,0 +1,10253 @@ +/*! + * jQuery JavaScript Library v3.2.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2017-03-20T18:59Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + + + + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.2.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && Array.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.3 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-08-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true && ("form" in elem || "label" in elem); + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + disabledAncestor( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: