diff --git a/.gitignore b/.gitignore index 06646e3..8acfb57 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ local*.cfg +build/ +python_bitcoinlib.egg-info/ diff --git a/BUGS b/BUGS deleted file mode 100644 index c332a7c..0000000 --- a/BUGS +++ /dev/null @@ -1,7 +0,0 @@ - -- Appears to have difficulty talking to nodes with protocol versions - between 40000 and 50001, inclusive. - -- Truncated or corrupted serialization may crash the program... - must test and see which exceptions are thrown. - diff --git a/COPYING b/COPYING deleted file mode 100644 index fec0301..0000000 --- a/COPYING +++ /dev/null @@ -1,22 +0,0 @@ -With the exception of bitcoin/rpc.py, which is licensed under the GNU LGPL, the -following license terms apply to the package python-bitcoinlib unless otherwise -noted: - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a4cfc1c --- /dev/null +++ b/LICENSE @@ -0,0 +1,177 @@ +python-bitcoinlib is free software: you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by the +Free Software Foundation, either version 3 of the License, or (at your option) +any later version. + +python-bitcoinlib is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +below for more details. + + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README b/README index dbc779c..8a8a8fe 100644 --- a/README +++ b/README @@ -1,10 +1,112 @@ +python-bitcoinlib +----------------- -This python library provides an easy interface to the bitcoin -data structures and protocol. +This Python2/3 library provides an easy interface to the bitcoin data +structures and protocol. The approach is low-level and "ground up", with a +focus on providing tools to manipulate the internals of how Bitcoin works. + +"The Swiss Army Knife of the Bitcoin protocol." - Wladimir J. van der Laan + + +Requirements +------------ + + sudo apt-get install libssl-dev + +The RPC interface, bitcoin.rpc, is designed to work with Bitcoin Core v0.9. +Older versions mostly work but there do exist some incompatibilities. + + +Structure +--------- + +Everything consensus critical is found in the modules under bitcoin.core. This +rule is followed pretty strictly, for instance chain parameters are split into +consensus critical and non-consensus-critical. + +bitcoin.core - Basic core definitions, datastructures, and + (context-independent) validation +bitcoin.core.key - ECC pubkeys +bitcoin.core.script - Scripts and opcodes +bitcoin.core.scripteval - Script evaluation/verification +bitcoin.core.serialize - Serialization + +In the future the bitcoin.core may use the Satoshi sourcecode directly as a +library. Non-consensus critical modules include the following: + +bitcoin - Chain selection +bitcoin.base58 - Base58 encoding +bitcoin.bloom - Bloom filters (incomplete) +bitcoin.net - Network communication (in flux) +bitcoin.messages - Network messages (in flux) +bitcoin.rpc - Bitcoin Core RPC interface support +bitcoin.wallet - Wallet-related code, currently Bitcoin address and private + key support + +Effort has been made to follow the Satoshi source relatively closely, for +instance Python code and classes that duplicate the functionality of +corresponding Satoshi C++ code uses the same naming conventions: CTransaction, +CBlockHeader, nValue etc. Otherwise Python naming conventions are followed. + + +Mutable vs. Immutable objects +----------------------------- + +Like the Bitcoin Core codebase CTransaction is immutable and +CMutableTransaction is mutable; unlike the Bitcoin Core codebase this +distinction also applies to COutPoint, CTxIn, CTxOut, and CBlock. + + +Endianness Gotchas +------------------ + +Rather confusingly Bitcoin Core shows transaction and block hashes as +little-endian hex rather than the big-endian the rest of the world uses for +SHA256. python-bitcoinlib provides the convenience functions x() and lx() in +bitcoin.core to convert from big-endian and little-endian hex to raw bytes to +accomodate this. In addition see b2x() and b2lx() for conversion from bytes to +big/little-endian hex. + + +Module import style +------------------- + +While not always good style, it's often convenient for quick scripts if import +* can be used. To support that all the modules have __all__ defined +appropriately. + + +Example Code +------------ + +See examples/ directory. For instance this example creates a transaction +spending a pay-to-script-hash transaction output: + + $ PYTHONPATH=. examples/spend-pay-to-script-hash-txout.py + + +Also see dust-b-gone for a simple example of Bitcoin Core wallet interaction +through the RPC interface: https://github.com/petertodd/dust-b-gone + + +Selecting the chain to use +-------------------------- + +Do the following: + + import bitcoin + bitcoin.SelectParams(NAME) + +Where NAME is one of 'testnet', 'mainnet', or 'regtest'. The chain currently +selected is a global variable that changes behavior everywhere, just like in +the Satoshi codebase. Unit tests ---------- -python -m unittest discover -python3 -m unittest discover +Under bitcoin/tests using test data from Bitcoin Core. To run them: + + python -m unittest discover && python3 -m unittest discover + +Please run the tests on both Python2 and Python3 for your pull-reqs! diff --git a/TODO b/TODO deleted file mode 100644 index 03d82f5..0000000 --- a/TODO +++ /dev/null @@ -1,14 +0,0 @@ - -See BUGS for other info. - -pay to script hash (P2SH, BIP 16) - -script opcodes: - OP_2ROT - -P2P commands: - alert - -Evaluate this checklist: -https://en.bitcoin.it/wiki/Protocol_rules#.22block.22_messages - diff --git a/bitcoin/__init__.py b/bitcoin/__init__.py index 27fe8df..4fade2f 100644 --- a/bitcoin/__init__.py +++ b/bitcoin/__init__.py @@ -1,4 +1,72 @@ -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# Copyright (C) 2012-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. from __future__ import absolute_import, division, print_function, unicode_literals + +import bitcoin.core + +class MainParams(bitcoin.core.CoreMainParams): + MESSAGE_START = b'\xf9\xbe\xb4\xd9' + DEFAULT_PORT = 8333 + RPC_PORT = 8332 + DNS_SEEDS = (('bitcoin.sipa.be', 'seed.bitcoin.sipa.be'), + ('bluematt.me', 'dnsseed.bluematt.me'), + ('dashjr.org', 'dnsseed.bitcoin.dashjr.org'), + ('bitcoinstats.com', 'seed.bitcoinstats.com'), + ('xf2.org', 'bitseed.xf2.org')) + BASE58_PREFIXES = {'PUBKEY_ADDR':0, + 'SCRIPT_ADDR':5, + 'SECRET_KEY' :128} + +class TestNetParams(bitcoin.core.CoreTestNetParams): + MESSAGE_START = b'\x0b\x11\x09\x07' + DEFAULT_PORT = 18333 + RPC_PORT = 18332 + DNS_SEEDS = (('bitcoin.petertodd.org', 'testnet-seed.bitcoin.petertodd.org'), + ('bluematt.me', 'testnet-seed.bluematt.me')) + BASE58_PREFIXES = {'PUBKEY_ADDR':111, + 'SCRIPT_ADDR':196, + 'SECRET_KEY' :239} + +class RegTestParams(bitcoin.core.CoreRegTestParams): + MESSAGE_START = b'\xfa\xbf\xb5\xda' + DEFAULT_PORT = 18444 + RPC_PORT = 18332 + DNS_SEEDS = () + BASE58_PREFIXES = {'PUBKEY_ADDR':111, + 'SCRIPT_ADDR':196, + 'SECRET_KEY' :239} + +"""Master global setting for what chain params we're using. + +However, don't set this directly, use SelectParams() instead so as to set the +bitcoin.core.params correctly too. +""" +#params = bitcoin.core.coreparams = MainParams() +params = MainParams() + +def SelectParams(name): + """Select the chain parameters to use + + name is one of 'mainnet', 'testnet', or 'regtest' + + Default chain is 'mainnet' + """ + global params + bitcoin.core._SelectCoreParams(name) + if name == 'mainnet': + params = bitcoin.core.coreparams = MainParams() + elif name == 'testnet': + params = bitcoin.core.coreparams = TestNetParams() + elif name == 'regtest': + params = bitcoin.core.coreparams = RegTestParams() + else: + raise ValueError('Unknown chain %r' % name) diff --git a/bitcoin/base58.py b/bitcoin/base58.py index 855f8bf..4253240 100644 --- a/bitcoin/base58.py +++ b/bitcoin/base58.py @@ -1,51 +1,68 @@ - +# Copyright (C) 2011 Sam Rushing +# Copyright (C) 2013-2014 The python-bitcoinlib developers # -# base58.py -# Original source: git://github.com/joric/brutus.git -# which was forked from git://github.com/samrushing/caesure.git +# This file is part of python-bitcoinlib. # -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. # +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Base58 encoding and decoding""" from __future__ import absolute_import, division, print_function, unicode_literals -from bitcoin.serialize import Hash, ser_uint256 +import sys +_bchr = chr +_bord = ord +if sys.version > '3': + long = int + _bchr = lambda x: bytes([x]) + _bord = lambda x: x + +import binascii -b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' +import bitcoin.core -from binascii import hexlify, unhexlify +B58_DIGITS = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' class Base58Error(Exception): pass class InvalidBase58Error(Base58Error): + """Raised on generic invalid base58 data, such as bad characters. + + Checksum failures raise Base58ChecksumError specifically. + """ pass def encode(b): """Encode bytes to a base58-encoded string""" # Convert big-endian bytes to integer - n = int('0x0' + hexlify(b).decode('utf8'), 16) + n = int('0x0' + binascii.hexlify(b).decode('utf8'), 16) # Divide that integer into bas58 res = [] while n > 0: - n, r = divmod (n, 58) - res.append(b58_digits[r]) + n, r = divmod(n, 58) + res.append(B58_DIGITS[r]) res = ''.join(res[::-1]) # Encode leading zeros as base58 zeros - import sys czero = b'\x00' if sys.version > '3': # In Python3 indexing a bytes returns numbers, not characters. czero = 0 pad = 0 for c in b: - if c == czero: pad += 1 - else: break - return b58_digits[0] * pad + res + if c == czero: + pad += 1 + else: + break + return B58_DIGITS[0] * pad + res def decode(s): """Decode a base58-encoding string, returning bytes""" @@ -56,54 +73,84 @@ def decode(s): n = 0 for c in s: n *= 58 - if c not in b58_digits: + if c not in B58_DIGITS: raise InvalidBase58Error('Character %r is not a valid base58 character' % c) - digit = b58_digits.index(c) + digit = B58_DIGITS.index(c) n += digit # Convert the integer to bytes h = '%x' % n if len(h) % 2: h = '0' + h - res = unhexlify(h.encode('utf8')) + res = binascii.unhexlify(h.encode('utf8')) # Add padding back. pad = 0 for c in s[:-1]: - if c == b58_digits[0]: pad += 1 + if c == B58_DIGITS[0]: pad += 1 else: break return b'\x00' * pad + res class Base58ChecksumError(Base58Error): + """Raised on Base58 checksum errors""" pass class CBase58Data(bytes): - def __new__(cls, data, nVersion): - self = super(CBase58Data, cls).__new__(cls, data) + """Base58-encoded data + + Includes a version and checksum. + """ + def __new__(cls, s): + k = decode(s) + verbyte, data, check0 = k[0:1], k[1:-4], k[-4:] + check1 = bitcoin.core.Hash(verbyte + data)[:4] + if check0 != check1: + raise Base58ChecksumError('Checksum mismatch: expected %r, calculated %r' % (check0, check1)) + + return cls.from_bytes(data, _bord(verbyte[0])) + + def __init__(self, s): + """Initialize from base58-encoded string + + Note: subclasses put your initialization routines here, but ignore the + argument - that's handled by __new__(), and .from_bytes() will call + __init__() with None in place of the string. + """ + + @classmethod + def from_bytes(cls, data, nVersion): + """Instantiate from data and nVersion""" + if not (0 <= nVersion <= 255): + raise ValueError('nVersion must be in range 0 to 255 inclusive; got %d' % nVersion) + self = bytes.__new__(cls, data) self.nVersion = nVersion + return self - def __repr__(self): - return '%s(%s, %d)' % (self.__class__.__name__, bytes.__repr__(self), self.nVersion) + def to_bytes(self): + """Convert to bytes instance + + Note that it's the data represented that is converted; the checkum and + nVersion is not included. + """ + return b'' + self def __str__(self): - vs = chr(self.nVersion) + self - check = ser_uint256(Hash(vs))[0:4] + """Convert to string""" + vs = _bchr(self.nVersion) + self + check = bitcoin.core.Hash(vs)[0:4] return encode(vs + check) - @classmethod - def from_str(cls, s): - k = decode(s) - addrbyte, data, check0 = k[0], k[1:-4], k[-4:] - check1 = ser_uint256(Hash(addrbyte + data))[:4] - if check0 != check1: - raise Base58ChecksumError('Checksum mismatch: expected %r, calculated %r' % (check0, check1)) - return cls(data, ord(addrbyte)) - - -class CBitcoinAddress(CBase58Data): - PUBKEY_ADDRESS = 0 - SCRIPT_ADDRESS = 5 - PUBKEY_ADDRESS_TEST = 111 - SCRIPT_ADDRESS_TEST = 196 + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, str(self)) + +__all__ = ( + 'B58_DIGITS', + 'Base58Error', + 'InvalidBase58Error', + 'encode', + 'decode', + 'Base58ChecksumError', + 'CBase58Data', +) diff --git a/bitcoin/bloom.py b/bitcoin/bloom.py index c562d08..8f76cbd 100644 --- a/bitcoin/bloom.py +++ b/bitcoin/bloom.py @@ -1,21 +1,91 @@ - +# Copyright (C) 2013-2014 The python-bitcoinlib developers # -# bloom.py +# This file is part of python-bitcoinlib. # -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. # +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Bloom filter support""" from __future__ import absolute_import, division, print_function, unicode_literals import struct +import sys import math -from bitcoin.serialize import * -from bitcoin.coredefs import * -from bitcoin.core import * -from bitcoin.hash import MurmurHash3 -class CBloomFilter(object): +import bitcoin.core +import bitcoin.core.serialize + +def _ROTL32(x, r): + assert x <= 0xFFFFFFFF + return ((x << r) & 0xFFFFFFFF) | (x >> (32 - r)) + +def MurmurHash3(nHashSeed, vDataToHash): + """MurmurHash3 (x86_32) + + Used for bloom filters. See http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + """ + + assert nHashSeed <= 0xFFFFFFFF + + h1 = nHashSeed + c1 = 0xcc9e2d51 + c2 = 0x1b873593 + + # body + i = 0 + while i < len(vDataToHash) - len(vDataToHash) % 4 \ + and len(vDataToHash) - i >= 4: + + k1 = struct.unpack(b" '3': + # In Py3 indexing bytes returns numbers, not characters + bord = lambda x: x + if len(vDataToHash) & 3 >= 3: + k1 ^= bord(vDataToHash[j+2]) << 16 + if len(vDataToHash) & 3 >= 2: + k1 ^= bord(vDataToHash[j+1]) << 8 + if len(vDataToHash) & 3 >= 1: + k1 ^= bord(vDataToHash[j]) + + k1 &= 0xFFFFFFFF + k1 = (k1 * c1) & 0xFFFFFFFF + k1 = _ROTL32(k1, 15) + k1 = (k1 * c2) & 0xFFFFFFFF + h1 ^= k1 + + # finalization + h1 ^= len(vDataToHash) & 0xFFFFFFFF + h1 ^= (h1 & 0xFFFFFFFF) >> 16 + h1 *= 0x85ebca6b + h1 ^= (h1 & 0xFFFFFFFF) >> 13 + h1 *= 0xc2b2ae35 + h1 ^= (h1 & 0xFFFFFFFF) >> 16 + + return h1 & 0xFFFFFFFF + + +class CBloomFilter(bitcoin.core.serialize.Serializable): # 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% MAX_BLOOM_FILTER_SIZE = 36000 MAX_HASH_FUNCS = 50 @@ -58,7 +128,7 @@ def insert(self, elem): elem may be a COutPoint or bytes """ - if isinstance(elem, COutPoint): + if isinstance(elem, bitcoin.core.COutPoint): elem = elem.serialize() if len(self.vData) == 1 and self.vData[0] == 0xff: @@ -74,7 +144,7 @@ def contains(self, elem): elem may be a COutPoint or bytes """ - if isinstance(elem, COutPoint): + if isinstance(elem, bitcoin.core.COutPoint): elem = elem.serialize() if len(self.vData) == 1 and self.vData[0] == 0xff: @@ -94,12 +164,28 @@ def IsRelevantAndUpdate(tx, tx_hash): raise NotImplementedError __struct = struct.Struct(b' '3': + bitcoin.core.serialize.BytesSerializer.stream_serialize(self.vData, f) + else: + # 2.7 has problems with f.write(bytearray()) + bitcoin.core.serialize.BytesSerializer.stream_serialize(bytes(self.vData), f) + f.write(self.__struct.pack(self.nHashFuncs, self.nTweak, self.nFlags)) + +__all__ = ( + 'MurmurHash3', + 'CBloomFilter', +) diff --git a/bitcoin/core.py b/bitcoin/core.py deleted file mode 100644 index 1d247a7..0000000 --- a/bitcoin/core.py +++ /dev/null @@ -1,348 +0,0 @@ - -# -# core.py -# -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -from __future__ import absolute_import, division, print_function, unicode_literals - -import struct -import socket -import binascii -import time -import hashlib -from bitcoin.serialize import * -from bitcoin.coredefs import * -from bitcoin.script import CScript - -class CAddress(object): - def __init__(self, protover=PROTO_VERSION): - self.protover = protover - self.nTime = 0 - self.nServices = 1 - self.pchReserved = b"\x00" * 10 + b"\xff" * 2 - self.ip = "0.0.0.0" - self.port = 0 - def deserialize(self, f): - if self.protover >= CADDR_TIME_VERSION: - self.nTime = struct.unpack(b"H", f.read(2))[0] - def serialize(self): - r = b"" - if self.protover >= CADDR_TIME_VERSION: - r += struct.pack(b"H", self.port) - return r - def __repr__(self): - return "CAddress(nTime=%d nServices=%i ip=%s port=%i)" % (self.nTime, self.nServices, self.ip, self.port) - -class CInv(object): - typemap = { - 0: "Error", - 1: "TX", - 2: "Block"} - def __init__(self): - self.type = 0 - self.hash = 0 - def deserialize(self, f): - self.type = struct.unpack(b" 1: - newhashes = [] - for i in range(0, len(hashes), 2): - i2 = min(i+1, len(hashes)-1) - newhashes.append(hashlib.sha256(hashlib.sha256(hashes[i] + hashes[i2]).digest()).digest()) - hashes = newhashes - return uint256_from_str(hashes[0]) - def is_valid(self): - self.calc_sha256() - target = uint256_from_compact(self.nBits) - if self.sha256 > target: - return False - if self.calc_merkle() != self.hashMerkleRoot: - return False - return True - def __repr__(self): - return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) - -class CUnsignedAlert(object): - def __init__(self): - self.nVersion = 1 - self.nRelayUntil = 0 - self.nExpiration = 0 - self.nID = 0 - self.nCancel = 0 - self.setCancel = [] - self.nMinVer = 0 - self.nMaxVer = 0 - self.setSubVer = [] - self.nPriority = 0 - self.strComment = b"" - self.strStatusBar = b"" - self.strReserved = b"" - def deserialize(self, f): - self.nVersion = struct.unpack(b" '3'): + x = _py2_x + b2x = _py2_b2x + lx = _py2_lx + b2lx = _py2_b2lx + +del _py2_x +del _py2_b2x +del _py2_lx +del _py2_b2lx + + +def str_money_value(value): + """Convert an integer money value to a fixed point string""" + r = '%i.%08i' % (value // COIN, value % COIN) + r = r.rstrip('0') + if r[-1] == '.': + r += '0' + return r + + +class ValidationError(Exception): + """Base class for all blockchain validation errors + + Everything that is related to validating the blockchain, blocks, + transactions, scripts, etc. is derived from this class. + """ + +def __make_mutable(cls): + # For speed we use a class decorator that removes the immutable + # restrictions directly. In addition the modified behavior of GetHash() and + # hash() is undone. + cls.__setattr__ = object.__setattr__ + cls.__delattr__ = object.__delattr__ + cls.GetHash = Serializable.GetHash + cls.__hash__ = Serializable.__hash__ + return cls + + +class COutPoint(ImmutableSerializable): + """The combination of a transaction hash and an index n into its vout""" + __slots__ = ['hash', 'n'] + + def __init__(self, hash=b'\x00'*32, n=0xffffffff): + if not len(hash) == 32: + raise ValueError('COutPoint: hash must be exactly 32 bytes; got %d bytes' % len(hash)) + object.__setattr__(self, 'hash', hash) + if not (0 <= n <= 0xffffffff): + raise ValueError('COutPoint: n must be in range 0x0 to 0xffffffff; got %x' % n) + object.__setattr__(self, 'n', n) + + @classmethod + def stream_deserialize(cls, f): + hash = ser_read(f,32) + n = struct.unpack(b"= 0: + return "CTxOut(%s*COIN, %r)" % (str_money_value(self.nValue), self.scriptPubKey) + else: + return "CTxOut(%d, %r)" % (self.nValue, self.scriptPubKey) + + @classmethod + def from_txout(cls, txout): + """Create an immutable copy of an existing TxOut + + If txout is already immutable (txout.__class__ is CTxOut) then it will + be returned directly. + """ + if txout.__class__ is CTxOut: + return txout + + else: + return cls(txout.nValue, txout.scriptPubKey) + +@__make_mutable +class CMutableTxOut(CTxOut): + """A mutable CTxOut""" + __slots__ = [] + + @classmethod + def from_txout(cls, txout): + """Create a fullly mutable copy of an existing TxOut""" + return cls(txout.nValue, txout.scriptPubKey) + +class CTransaction(ImmutableSerializable): + """A transaction""" + __slots__ = ['nVersion', 'vin', 'vout', 'nLockTime'] + + def __init__(self, vin=(), vout=(), nLockTime=0, nVersion=1): + """Create a new transaction + + vin and vout are iterables of transaction inputs and outputs + respectively. If their contents are not already immutable, immutable + copies will be made. + """ + if not (0 <= nLockTime <= 0xffffffff): + raise ValueError('CTransaction: nLockTime must be in range 0x0 to 0xffffffff; got %x' % nLockTime) + object.__setattr__(self, 'nLockTime', nLockTime) + + object.__setattr__(self, 'nVersion', nVersion) + object.__setattr__(self, 'vin', tuple(CTxIn.from_txin(txin) for txin in vin)) + object.__setattr__(self, 'vout', tuple(CTxOut.from_txout(txout) for txout in vout)) + + @classmethod + def stream_deserialize(cls, f): + nVersion = struct.unpack(b"> 24) & 0xff + dDiff = float(0x0000ffff) / float(nBits & 0x00ffffff) + while nShift < 29: + dDiff *= 256.0 + nShift += 1 + while nShift > 29: + dDiff /= 256.0 + nShift -= 1 + return dDiff + difficulty = property(lambda self: CBlockHeader.calc_difficulty(self.nBits)) + + def __repr__(self): + return "%s(%i, lx(%s), lx(%s), %s, 0x%08x, 0x%08x)" % \ + (self.__class__.__name__, self.nVersion, b2lx(self.hashPrevBlock), b2lx(self.hashMerkleRoot), + self.nTime, self.nBits, self.nNonce) + +class CBlock(CBlockHeader): + """A block including all transactions in it""" + __slots__ = ['vtx', 'vMerkleTree'] + + @staticmethod + def build_merkle_tree_from_txids(txids): + """Build a full CBlock merkle tree from txids + + txids - iterable of txids + + Returns a new merkle tree in deepest first order. The last element is + the merkle root. + + WARNING! If you're reading this because you're learning about crypto + and/or designing a new system that will use merkle trees, keep in mind + that the following merkle tree algorithm has a serious flaw related to + duplicate txids, resulting in a vulnerability. (CVE-2012-2459) Bitcoin + has since worked around the flaw, but for new applications you should + use something different; don't just copy-and-paste this code without + understanding the problem first. + """ + merkle_tree = list(txids) + + size = len(txids) + j = 0 + while size > 1: + for i in range(0, size, 2): + i2 = min(i+1, size-1) + merkle_tree.append(Hash(merkle_tree[j+i] + merkle_tree[j+i2])) + + j += size + size = (size + 1) // 2 + + return merkle_tree + + @staticmethod + def build_merkle_tree_from_txs(txs): + """Build a full merkle tree from transactions""" + txids = [tx.GetHash() for tx in txs] + return CBlock.build_merkle_tree_from_txids(txids) + + def calc_merkle_root(self): + """Calculate the merkle root + + The calculated merkle root is not cached; every invocation + re-calculates it from scratch. + """ + if not len(self.vtx): + raise ValueError('Block contains no transactions') + return self.build_merkle_tree_from_txs(self.vtx)[-1] + + def __init__(self, nVersion=2, hashPrevBlock=b'\x00'*32, hashMerkleRoot=b'\x00'*32, nTime=0, nBits=0, nNonce=0, vtx=()): + """Create a new block""" + super(CBlock, self).__init__(nVersion, hashPrevBlock, hashMerkleRoot, nTime, nBits, nNonce) + + vMerkleTree = tuple(CBlock.build_merkle_tree_from_txs(vtx)) + object.__setattr__(self, 'vMerkleTree', vMerkleTree) + object.__setattr__(self, 'vtx', tuple(CTransaction.from_tx(tx) for tx in vtx)) + + @classmethod + def stream_deserialize(cls, f): + self = super(CBlock, cls).stream_deserialize(f) + + vtx = VectorSerializer.stream_deserialize(CTransaction, f) + vMerkleTree = tuple(CBlock.build_merkle_tree_from_txs(vtx)) + object.__setattr__(self, 'vMerkleTree', vMerkleTree) + object.__setattr__(self, 'vtx', tuple(vtx)) + + return self + + def stream_serialize(self, f): + super(CBlock, self).stream_serialize(f) + VectorSerializer.stream_serialize(CTransaction, self.vtx, f) + + def get_header(self): + """Return the block header + + Returned header is a new object. + """ + return CBlockHeader(nVersion=self.nVersion, + hashPrevBlock=self.hashPrevBlock, + hashMerkleRoot=self.hashMerkleRoot, + nTime=self.nTime, + nBits=self.nBits, + nNonce=self.nNonce) + + def GetHash(self): + """Return the block hash + + Note that this is the hash of the header, not the entire serialized + block. + """ + try: + return self._cached_GetHash + except AttributeError: + _cached_GetHash = self.get_header().GetHash() + object.__setattr__(self, '_cached_GetHash', _cached_GetHash) + return _cached_GetHash + +class CoreChainParams(object): + """Define consensus-critical parameters of a given instance of the Bitcoin system""" + MAX_MONEY = None + GENESIS_BLOCK = None + PROOF_OF_WORK_LIMIT = None + SUBSIDY_HALVING_INTERVAL = None + NAME = None + +class CoreMainParams(CoreChainParams): + MAX_MONEY = 21000000 * COIN + NAME = 'mainnet' + GENESIS_BLOCK = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) + SUBSIDY_HALVING_INTERVAL = 210000 + PROOF_OF_WORK_LIMIT = 2**256-1 >> 32 + +class CoreTestNetParams(CoreMainParams): + NAME = 'testnet' + GENESIS_BLOCK = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) + +class CoreRegTestParams(CoreTestNetParams): + NAME = 'regtest' + GENESIS_BLOCK = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff7f20020000000101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) + SUBSIDY_HALVING_INTERVAL = 150 + PROOF_OF_WORK_LIMIT = 2**256-1 >> 1 + +"""Master global setting for what core chain params we're using""" +coreparams = CoreMainParams() + +def _SelectCoreParams(name): + """Select the core chain parameters to use + + Don't use this directly, use bitcoin.SelectParams() instead so both + consensus-critical and general parameters are set properly. + """ + global coreparams + if name == 'mainnet': + coreparams = CoreMainParams() + elif name == 'testnet': + coreparams = CoreTestNetParams() + elif name == 'regtest': + coreparams = CoreRegTestParams() + else: + raise ValueError('Unknown chain %r' % name) + + +class CheckTransactionError(ValidationError): + pass + +def CheckTransaction(tx): + """Basic transaction checks that don't depend on any context. + + Raises CheckTransactionError + """ + global coreparams + + if not tx.vin: + raise CheckTransactionError("CheckTransaction() : vin empty") + if not tx.vout: + raise CheckTransactionError("CheckTransaction() : vout empty") + + # Size limits + if len(tx.serialize()) > MAX_BLOCK_SIZE: + raise CheckTransactionError("CheckTransaction() : size limits failed") + + # Check for negative or overflow output values + nValueOut = 0 + for txout in tx.vout: + if txout.nValue < 0: + raise CheckTransactionError("CheckTransaction() : txout.nValue negative") + if txout.nValue > coreparams.MAX_MONEY: + raise CheckTransactionError("CheckTransaction() : txout.nValue too high") + nValueOut += txout.nValue + if not MoneyRange(nValueOut): + raise CheckTransactionError("CheckTransaction() : txout total out of range") + + # Check for duplicate inputs + vin_outpoints = set() + for txin in tx.vin: + if txin.prevout in vin_outpoints: + raise CheckTransactionError("CheckTransaction() : duplicate inputs") + vin_outpoints.add(txin.prevout) + + if tx.is_coinbase(): + if not (2 <= len(tx.vin[0].scriptSig) <= 100): + raise CheckTransactionError("CheckTransaction() : coinbase script size") + + else: + for txin in tx.vin: + if txin.prevout.is_null(): + raise CheckTransactionError("CheckTransaction() : prevout is null") + + + + + +class CheckBlockHeaderError(ValidationError): + pass + +class CheckProofOfWorkError(CheckBlockHeaderError): + pass + +def CheckProofOfWork(hash, nBits): + """Check a proof-of-work + + Raises CheckProofOfWorkError + """ + target = uint256_from_compact(nBits) + + # Check range + if not (0 < target <= coreparams.PROOF_OF_WORK_LIMIT): + raise CheckProofOfWorkError("CheckProofOfWork() : nBits below minimum work") + + # Check proof of work matches claimed amount + hash = uint256_from_str(hash) + if hash > target: + raise CheckProofOfWorkError("CheckProofOfWork() : hash doesn't match nBits") + + +def CheckBlockHeader(block_header, fCheckPoW = True, cur_time=None): + """Context independent CBlockHeader checks. + + fCheckPoW - Check proof-of-work. + cur_time - Current time. Defaults to time.time() + + Raises CBlockHeaderError if block header is invalid. + """ + if cur_time is None: + cur_time = time.time() + + # Check proof-of-work matches claimed amount + if fCheckPoW: + CheckProofOfWork(block_header.GetHash(), block_header.nBits) + + # Check timestamp + if block_header.nTime > cur_time + 2 * 60 * 60: + raise CheckBlockHeaderError("CheckBlockHeader() : block timestamp too far in the future") + + +class CheckBlockError(CheckBlockHeaderError): + pass + +def GetLegacySigOpCount(tx): + nSigOps = 0 + for txin in tx.vin: + nSigOps += txin.scriptSig.GetSigOpCount(False) + for txout in tx.vout: + nSigOps += txout.scriptPubKey.GetSigOpCount(False) + return nSigOps + + +def CheckBlock(block, fCheckPoW = True, fCheckMerkleRoot = True, cur_time=None): + """Context independent CBlock checks. + + CheckBlockHeader() is called first, which may raise a CheckBlockHeader + exception, followed the block tests. CheckTransaction() is called for every + transaction. + + fCheckPoW - Check proof-of-work. + fCheckMerkleRoot - Check merkle root matches transactions. + cur_time - Current time. Defaults to time.time() + """ + + # Block header checks + CheckBlockHeader(block.get_header(), fCheckPoW=fCheckPoW, cur_time=cur_time) + + # Size limits + if not block.vtx: + raise CheckBlockError("CheckBlock() : vtx empty") + if len(block.serialize()) > MAX_BLOCK_SIZE: + raise CheckBlockError("CheckBlock() : block larger than MAX_BLOCK_SIZE") + + # First transaction must be coinbase + if not block.vtx[0].is_coinbase(): + raise CheckBlockError("CheckBlock() : first tx is not coinbase") + + # Check rest of transactions. Note how we do things "all at once", which + # could potentially be a consensus failure if there was some obscure bug. + + # For unique txid uniqueness testing. If coinbase tx is included twice + # it'll be caught by the "more than one coinbase" test. + unique_txids = set() + nSigOps = 0 + for tx in block.vtx[1:]: + if tx.is_coinbase(): + raise CheckBlockError("CheckBlock() : more than one coinbase") + + CheckTransaction(tx) + + txid = tx.GetHash() + if txid in unique_txids: + raise CheckBlockError("CheckBlock() : duplicate transaction") + unique_txids.add(txid) + + nSigOps += GetLegacySigOpCount(tx) + if nSigOps > MAX_BLOCK_SIGOPS: + raise CheckBlockError("CheckBlock() : out-of-bounds SigOpCount") + + # Check merkle root + if fCheckMerkleRoot and block.hashMerkleRoot != block.calc_merkle_root(): + raise CheckBlockError("CheckBlock() : hashMerkleRoot mismatch") + +__all__ = ( + 'Hash', + 'Hash160', + 'COIN', + 'MAX_BLOCK_SIZE', + 'MAX_BLOCK_SIGOPS', + 'MoneyRange', + 'x', + 'b2x', + 'lx', + 'b2lx', + 'str_money_value', + 'ValidationError', + 'COutPoint', + 'CMutableOutPoint', + 'CTxIn', + 'CMutableTxIn', + 'CTxOut', + 'CMutableTxOut', + 'CTransaction', + 'CMutableTransaction', + 'CBlockHeader', + 'CBlock', + 'CoreChainParams', + 'CoreMainParams', + 'CoreTestNetParams', + 'CoreRegTestParams', + 'CheckTransactionError', + 'CheckTransaction', + 'CheckBlockHeaderError', + 'CheckProofOfWorkError', + 'CheckProofOfWork', + 'CheckBlockHeader', + 'CheckBlockError', + 'GetLegacySigOpCount', + 'CheckBlock', +) diff --git a/bitcoin/bignum.py b/bitcoin/core/_bignum.py similarity index 72% rename from bitcoin/bignum.py rename to bitcoin/core/_bignum.py index 2dbe662..42985a7 100644 --- a/bitcoin/bignum.py +++ b/bitcoin/core/_bignum.py @@ -1,10 +1,17 @@ - +# Copyright (C) 2012-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. # -# bignum.py +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. # -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +# Bignum routines # +# Internally used for script evaluation; not to be used externally. from __future__ import absolute_import, division, print_function, unicode_literals @@ -17,13 +24,13 @@ def bn_bytes(v, have_ext=False): ext = 0 if have_ext: ext = 1 - return ((v.bit_length()+7)//8) + ext + 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) + s.append((v >> ((i - 1) * 8)) & 0xff) i -= 1 return s @@ -58,7 +65,7 @@ def bn2mpi(v): def mpi2bn(s): if len(s) < 4: return None - s_size = str(s[:4]) + s_size = bytes(s[:4]) v_len = struct.unpack(b">I", s_size)[0] if len(s) != (v_len + 4): return None @@ -86,7 +93,7 @@ def mpi2vch(s): return r def bn2vch(v): - return str(mpi2vch(bn2mpi(v))) + return bytes(mpi2vch(bn2mpi(v))) def vch2mpi(s): r = struct.pack(b">I", len(s)) # size diff --git a/bitcoin/core/key.py b/bitcoin/core/key.py new file mode 100644 index 0000000..6891819 --- /dev/null +++ b/bitcoin/core/key.py @@ -0,0 +1,232 @@ +# Copyright (C) 2011 Sam Rushing +# Copyright (C) 2012-2015 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""ECC secp256k1 crypto routines + +WARNING: This module does not mlock() secrets; your private keys may end up on +disk in swap! Use with caution! +""" + +import ctypes +import ctypes.util +import hashlib +import sys + +import bitcoin.core.script + +_ssl = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ssl') or 'libeay32') + +# this specifies the curve used with ECDSA. +_NID_secp256k1 = 714 # from openssl/obj_mac.h + +# test that openssl support secp256k1 +if _ssl.EC_KEY_new_by_curve_name(_NID_secp256k1) == 0: + errno = _ssl.ERR_get_error() + errmsg = ctypes.create_string_buffer(120) + _ssl.ERR_error_string_n(errno, errmsg, 120) + raise RuntimeError('openssl error: %s' % errmsg.value) + +# Thx to Sam Devlin for the ctypes magic 64-bit fix. +def _check_result (val, func, args): + if val == 0: + raise ValueError + else: + return ctypes.c_void_p(val) + +_ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p +_ssl.EC_KEY_new_by_curve_name.errcheck = _check_result + +# From openssl/ecdsa.h +class ECDSA_SIG_st(ctypes.Structure): + _fields_ = [("r", ctypes.c_void_p), + ("s", ctypes.c_void_p)] + +class CECKey: + """Wrapper around OpenSSL's EC_KEY""" + + POINT_CONVERSION_COMPRESSED = 2 + POINT_CONVERSION_UNCOMPRESSED = 4 + + def __init__(self): + self.k = _ssl.EC_KEY_new_by_curve_name(_NID_secp256k1) + + def __del__(self): + if _ssl: + _ssl.EC_KEY_free(self.k) + self.k = None + + def set_secretbytes(self, secret): + priv_key = _ssl.BN_bin2bn(secret, 32, _ssl.BN_new()) + group = _ssl.EC_KEY_get0_group(self.k) + pub_key = _ssl.EC_POINT_new(group) + ctx = _ssl.BN_CTX_new() + if not _ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx): + raise ValueError("Could not derive public key from the supplied secret.") + _ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx) + _ssl.EC_KEY_set_private_key(self.k, priv_key) + _ssl.EC_KEY_set_public_key(self.k, pub_key) + _ssl.EC_POINT_free(pub_key) + _ssl.BN_CTX_free(ctx) + return self.k + + def set_privkey(self, key): + self.mb = ctypes.create_string_buffer(key) + return _ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key)) + + def set_pubkey(self, key): + self.mb = ctypes.create_string_buffer(key) + return _ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key)) + + def get_privkey(self): + size = _ssl.i2d_ECPrivateKey(self.k, 0) + mb_pri = ctypes.create_string_buffer(size) + _ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri))) + return mb_pri.raw + + def get_pubkey(self): + size = _ssl.i2o_ECPublicKey(self.k, 0) + mb = ctypes.create_string_buffer(size) + _ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb))) + return mb.raw + + def get_raw_ecdh_key(self, other_pubkey): + ecdh_keybuffer = ctypes.create_string_buffer(32) + r = _ssl.ECDH_compute_key(ctypes.pointer(ecdh_keybuffer), 32, + _ssl.EC_KEY_get0_public_key(other_pubkey.k), + self.k, 0) + if r != 32: + raise Exception('CKey.get_ecdh_key(): ECDH_compute_key() failed') + return ecdh_keybuffer.raw + + def get_ecdh_key(self, other_pubkey, kdf=lambda k: hashlib.sha256(k).digest()): + # FIXME: be warned it's not clear what the kdf should be as a default + r = self.get_raw_ecdh_key(other_pubkey) + return kdf(r) + + def sign(self, hash): + if not isinstance(hash, bytes): + raise TypeError('Hash must be bytes instance; got %r' % hash.__class__) + if len(hash) != 32: + raise ValueError('Hash must be exactly 32 bytes long') + + sig_size0 = ctypes.c_uint32() + sig_size0.value = _ssl.ECDSA_size(self.k) + mb_sig = ctypes.create_string_buffer(sig_size0.value) + result = _ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k) + assert 1 == result + if bitcoin.core.script.IsLowDERSignature(mb_sig.raw[:sig_size0.value]): + return mb_sig.raw[:sig_size0.value] + else: + return self.signature_to_low_s(mb_sig.raw[:sig_size0.value]) + + def signature_to_low_s(self, sig): + der_sig = ECDSA_SIG_st() + _ssl.d2i_ECDSA_SIG(ctypes.byref(ctypes.pointer(der_sig)), ctypes.byref(ctypes.c_char_p(sig)), len(sig)) + group = _ssl.EC_KEY_get0_group(self.k) + order = _ssl.BN_new() + halforder = _ssl.BN_new() + ctx = _ssl.BN_CTX_new() + _ssl.EC_GROUP_get_order(group, order, ctx) + _ssl.BN_rshift1(halforder, order) + + # Verify that s is over half the order of the curve before we actually subtract anything from it + if _ssl.BN_cmp(der_sig.s, halforder) > 0: + _ssl.BN_sub(der_sig.s, order, der_sig.s) + + _ssl.BN_free(halforder) + _ssl.BN_free(order) + _ssl.BN_CTX_free(ctx) + + derlen = _ssl.i2d_ECDSA_SIG(ctypes.pointer(der_sig), 0) + if derlen == 0: + _ssl.ECDSA_SIG_free(der_sig) + return None + new_sig = ctypes.create_string_buffer(derlen) + _ssl.i2d_ECDSA_SIG(ctypes.pointer(der_sig), ctypes.byref(ctypes.pointer(new_sig))) + _ssl.BN_free(der_sig.r) + _ssl.BN_free(der_sig.s) + + return new_sig.raw + + def verify(self, hash, sig): + """Verify a DER signature""" + if not sig: + return false + + # New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. + norm_sig = ctypes.c_void_p(0) + _ssl.d2i_ECDSA_SIG(ctypes.byref(norm_sig), ctypes.byref(ctypes.c_char_p(sig)), len(sig)) + + derlen = _ssl.i2d_ECDSA_SIG(norm_sig, 0) + if derlen == 0: + _ssl.ECDSA_SIG_free(norm_sig) + return false + + norm_der = ctypes.create_string_buffer(derlen) + _ssl.i2d_ECDSA_SIG(norm_sig, ctypes.byref(ctypes.pointer(norm_der))) + _ssl.ECDSA_SIG_free(norm_sig) + + # -1 = error, 0 = bad sig, 1 = good + return _ssl.ECDSA_verify(0, hash, len(hash), norm_der, derlen, self.k) == 1 + + def set_compressed(self, compressed): + if compressed: + form = self.POINT_CONVERSION_COMPRESSED + else: + form = self.POINT_CONVERSION_UNCOMPRESSED + _ssl.EC_KEY_set_conv_form(self.k, form) + + +class CPubKey(bytes): + """An encapsulated public key + + Attributes: + + is_valid - Corresponds to CPubKey.IsValid() + is_fullyvalid - Corresponds to CPubKey.IsFullyValid() + is_compressed - Corresponds to CPubKey.IsCompressed() + """ + + def __new__(cls, buf, _cec_key=None): + self = super(CPubKey, cls).__new__(cls, buf) + if _cec_key is None: + _cec_key = CECKey() + self._cec_key = _cec_key + self.is_fullyvalid = _cec_key.set_pubkey(self) != 0 + return self + + @property + def is_valid(self): + return len(self) > 0 + + @property + def is_compressed(self): + return len(self) == 33 + + def verify(self, hash, sig): + return self._cec_key.verify(hash, sig) + + def __str__(self): + return repr(self) + + def __repr__(self): + # Always have represent as b'' so test cases don't have to + # change for py2/3 + if sys.version > '3': + return '%s(%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) + else: + return '%s(b%s)' % (self.__class__.__name__, super(CPubKey, self).__repr__()) + +__all__ = ( + 'CECKey', + 'CPubKey', +) diff --git a/bitcoin/core/script.py b/bitcoin/core/script.py new file mode 100644 index 0000000..2bf4267 --- /dev/null +++ b/bitcoin/core/script.py @@ -0,0 +1,1069 @@ +# Copyright (C) 2012-2015 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Scripts + +Functionality to build scripts, as well as SignatureHash(). Script evaluation +is in bitcoin.core.scripteval +""" + +from __future__ import absolute_import, division, print_function + +import sys +_bchr = chr +_bord = ord +if sys.version > '3': + long = int + _bchr = lambda x: bytes([x]) + _bord = lambda x: x + +import array +import copy +import struct + +import bitcoin.core +import bitcoin.core._bignum + +MAX_SCRIPT_SIZE = 10000 +MAX_SCRIPT_ELEMENT_SIZE = 520 +MAX_SCRIPT_OPCODES = 201 + +OPCODE_NAMES = {} + +_opcode_instances = [] +class CScriptOp(int): + """A single script opcode""" + __slots__ = [] + + @staticmethod + def encode_op_pushdata(d): + """Encode a PUSHDATA op, returning bytes""" + if len(d) < 0x4c: + return b'' + _bchr(len(d)) + d # OP_PUSHDATA + elif len(d) <= 0xff: + return b'\x4c' + _bchr(len(d)) + d # OP_PUSHDATA1 + elif len(d) <= 0xffff: + return b'\x4d' + struct.pack(b' OP_PUSHDATA4: + yield (opcode, None, sop_idx) + else: + datasize = None + pushdata_type = None + if opcode < OP_PUSHDATA1: + pushdata_type = 'PUSHDATA(%d)' % opcode + datasize = opcode + + elif opcode == OP_PUSHDATA1: + pushdata_type = 'PUSHDATA1' + if i >= len(self): + raise CScriptInvalidError('PUSHDATA1: missing data length') + datasize = _bord(self[i]) + i += 1 + + elif opcode == OP_PUSHDATA2: + pushdata_type = 'PUSHDATA2' + if i + 1 >= len(self): + raise CScriptInvalidError('PUSHDATA2: missing data length') + datasize = _bord(self[i]) + (_bord(self[i+1]) << 8) + i += 2 + + elif opcode == OP_PUSHDATA4: + pushdata_type = 'PUSHDATA4' + if i + 3 >= len(self): + raise CScriptInvalidError('PUSHDATA4: missing data length') + datasize = _bord(self[i]) + (_bord(self[i+1]) << 8) + (_bord(self[i+2]) << 16) + (_bord(self[i+3]) << 24) + i += 4 + + else: + assert False # shouldn't happen + + + data = bytes(self[i:i+datasize]) + + # Check for truncation + if len(data) < datasize: + raise CScriptTruncatedPushDataError('%s: truncated data' % pushdata_type, data) + + i += datasize + + yield (opcode, data, sop_idx) + + def __iter__(self): + """'Cooked' iteration + + Returns either a CScriptOP instance, an integer, or bytes, as + appropriate. + + See raw_iter() if you need to distinguish the different possible + PUSHDATA encodings. + """ + for (opcode, data, sop_idx) in self.raw_iter(): + if data is not None: + yield data + else: + opcode = CScriptOp(opcode) + + if opcode.is_small_int(): + yield opcode.decode_op_n() + else: + yield CScriptOp(opcode) + + def __repr__(self): + # For Python3 compatibility add b before strings so testcases don't + # need to change + def _repr(o): + if isinstance(o, bytes): + return "x('%s')" % bitcoin.core.b2x(o) + else: + return repr(o) + + ops = [] + i = iter(self) + while True: + op = None + try: + op = _repr(next(i)) + except CScriptTruncatedPushDataError as err: + op = '%s...' % (_repr(err.data), err) + break + except CScriptInvalidError as err: + op = '' % err + break + except StopIteration: + break + finally: + if op is not None: + ops.append(op) + + return "CScript([%s])" % ', '.join(ops) + + def is_p2sh(self): + """Test if the script is a p2sh scriptPubKey + + Note that this test is consensus-critical. + """ + return (len(self) == 23 and + _bord(self[0]) == OP_HASH160 and + _bord(self[1]) == 0x14 and + _bord(self[22]) == OP_EQUAL) + + def is_push_only(self): + """Test if the script only contains pushdata ops + + Note that this test is consensus-critical. + + Scripts that contain invalid pushdata ops return False, matching the + behavior in Bitcoin Core. + """ + try: + for (op, op_data, idx) in self.raw_iter(): + # Note how OP_RESERVED is considered a pushdata op. + if op > OP_16: + return False + + except CScriptInvalidError: + return False + return True + + def has_canonical_pushes(self): + """Test if script only uses canonical pushes + + Not yet consensus critical; may be in the future. + """ + try: + for (op, data, idx) in self.raw_iter(): + if op > OP_16: + continue + + elif op < OP_PUSHDATA1 and op > OP_0 and len(data) == 1 and _bord(data[0]) <= 16: + # Could have used an OP_n code, rather than a 1-byte push. + return False + + elif op == OP_PUSHDATA1 and len(data) < OP_PUSHDATA1: + # Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return False + + elif op == OP_PUSHDATA2 and len(data) <= 0xFF: + # Could have used a OP_PUSHDATA1. + return False + + elif op == OP_PUSHDATA4 and len(data) <= 0xFFFF: + # Could have used a OP_PUSHDATA2. + return False + + except CScriptInvalidError: # Invalid pushdata + return False + return True + + def is_unspendable(self): + """Test if the script is provably unspendable""" + return (len(self) > 0 and + _bord(self[0]) == OP_RETURN) + + def is_valid(self): + """Return True if the script is valid, False otherwise + + The script is valid if all PUSHDATA's are valid; invalid opcodes do not + make is_valid() return False. + """ + try: + list(self) + except CScriptInvalidError: + return False + return True + + def to_p2sh_scriptPubKey(self, checksize=True): + """Create P2SH scriptPubKey from this redeemScript + + That is, create the P2SH scriptPubKey that requires this script as a + redeemScript to spend. + + checksize - Check if the redeemScript is larger than the 520-byte max + pushdata limit; raise ValueError if limit exceeded. + + Since a >520-byte PUSHDATA makes EvalScript() fail, it's not actually + possible to redeem P2SH outputs with redeem scripts >520 bytes. + """ + if checksize and len(self) > MAX_SCRIPT_ELEMENT_SIZE: + raise ValueError("redeemScript exceeds max allowed size; P2SH output would be unspendable") + return CScript([OP_HASH160, bitcoin.core.Hash160(self), OP_EQUAL]) + + def GetSigOpCount(self, fAccurate): + """Get the SigOp count. + + fAccurate - Accurately count CHECKMULTISIG, see BIP16 for details. + + Note that this is consensus-critical. + """ + n = 0 + lastOpcode = OP_INVALIDOPCODE + for (opcode, data, sop_idx) in self.raw_iter(): + if opcode in (OP_CHECKSIG, OP_CHECKSIGVERIFY): + n += 1 + elif opcode in (OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY): + if fAccurate and (OP_1 <= lastOpcode <= OP_16): + n += opcode.decode_op_n() + else: + n += 20 + lastOpcode = opcode + return n + + +SCRIPT_VERIFY_P2SH = object() +SCRIPT_VERIFY_STRICTENC = object() +SCRIPT_VERIFY_EVEN_S = object() +SCRIPT_VERIFY_NOCACHE = object() + +SIGHASH_ALL = 1 +SIGHASH_NONE = 2 +SIGHASH_SINGLE = 3 +SIGHASH_ANYONECANPAY = 0x80 + +def FindAndDelete(script, sig): + """Consensus critical, see FindAndDelete() in Satoshi codebase""" + r = b'' + last_sop_idx = sop_idx = 0 + skip = True + for (opcode, data, sop_idx) in script.raw_iter(): + if not skip: + r += script[last_sop_idx:sop_idx] + last_sop_idx = sop_idx + if script[sop_idx:sop_idx + len(sig)] == sig: + skip = True + else: + skip = False + if not skip: + r += script[last_sop_idx:] + return CScript(r) + +def IsLowDERSignature(sig): + """ + Loosely correlates with IsLowDERSignature() from script/interpreter.cpp + Verifies that the S value in a DER signature is the lowest possible value. + Used by BIP62 malleability fixes. + """ + length_r = sig[3] + if isinstance(length_r, str): + length_r = int(struct.unpack('B', length_r)[0]) + length_s = sig[5 + length_r] + if isinstance(length_s, str): + length_s = int(struct.unpack('B', length_s)[0]) + s_val = list(struct.unpack(str(length_s) + 'B', sig[6 + length_r:6 + length_r + length_s])) + + # If the S value is above the order of the curve divided by two, its + # complement modulo the order could have been used instead, which is + # one byte shorter when encoded correctly. + max_mod_half_order = [ + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d, + 0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0] + + return CompareBigEndian(s_val, [0]) > 0 and \ + CompareBigEndian(s_val, max_mod_half_order) <= 0 + +def CompareBigEndian(c1, c2): + """ + Loosely matches CompareBigEndian() from eccryptoverify.cpp + Compares two arrays of bytes, and returns a negative value if the first is + less than the second, 0 if they're equal, and a positive value if the + first is greater than the second. + """ + c1 = list(c1) + c2 = list(c2) + + # Adjust starting positions until remaining lengths of the two arrays match + while len(c1) > len(c2): + if c1.pop(0) > 0: + return 1 + while len(c2) > len(c1): + if c2.pop(0) > 0: + return -1 + + while len(c1) > 0: + diff = c1.pop(0) - c2.pop(0) + if diff != 0: + return diff + + return 0 + + +def RawSignatureHash(script, txTo, inIdx, hashtype): + """Consensus-correct SignatureHash + + Returns (hash, err) to precisely match the consensus-critical behavior of + the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity) + + If you're just writing wallet software you probably want SignatureHash() + instead. + """ + HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + + if inIdx >= len(txTo.vin): + return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) + + #txtmp = bitcoin.core.CTransaction() + #txtmp.vin = [bitcoin.core.CTxIn(txin.prevout, txin.scriptSig) for txin in txTo.vin] + #txtmp.vout = list(txTo.vout) + #txtmp = bitcoin.core.CTransaction.deserialize(txTo.serialize()) + txtmp = bitcoin.core.CMutableTransaction.from_tx(txTo) + + for txin in txtmp.vin: + txin.scriptSig = b'' + txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR])) + + if (hashtype & 0x1f) == SIGHASH_NONE: + txtmp.vout = [] + + for i in range(len(txtmp.vin)): + if i != inIdx: + txtmp.vin[i].nSequence = 0 + + elif (hashtype & 0x1f) == SIGHASH_SINGLE: + outIdx = inIdx + if outIdx >= len(txtmp.vout): + return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) + + tmp = txtmp.vout[outIdx] + txtmp.vout = [] + for i in range(outIdx): + txtmp.vout.append(bitcoin.core.CTxOut()) + txtmp.vout.append(tmp) + + for i in range(len(txtmp.vin)): + if i != inIdx: + txtmp.vin[i].nSequence = 0 + + if hashtype & SIGHASH_ANYONECANPAY: + tmp = txtmp.vin[inIdx] + txtmp.vin = [] + txtmp.vin.append(tmp) + + s = txtmp.serialize() + s += struct.pack(b" '3': + long = int + _bord = lambda x: x + +import copy +import hashlib + +import bitcoin.core +import bitcoin.core._bignum +import bitcoin.core.key +import bitcoin.core.serialize + +# Importing everything for simplicity; note that we use __all__ at the end so +# we're not exporting the whole contents of the script module. +from bitcoin.core.script import * + +MAX_NUM_SIZE = 4 +MAX_STACK_ITEMS = 1000 + +SCRIPT_VERIFY_P2SH = object() +SCRIPT_VERIFY_STRICTENC = object() +SCRIPT_VERIFY_EVEN_S = object() +SCRIPT_VERIFY_NOCACHE = object() + +class EvalScriptError(bitcoin.core.ValidationError): + """Base class for exceptions raised when a script fails during EvalScript() + + The execution state just prior the opcode raising the is saved. (if + available) + """ + def __init__(self, + msg, + sop=None, sop_data=None, sop_pc=None, + stack=None, scriptIn=None, txTo=None, inIdx=None, flags=None, + altstack=None, vfExec=None, pbegincodehash=None, nOpCount=None): + super(EvalScriptError, self).__init__('EvalScript: %s' % msg) + + sop = sop + sop_data = sop_data + sop_pc = sop_pc + stack = stack + scriptIn = scriptIn + txTo = txTo + inIdx = inIdx + flags = flags + altstack = altstack + vfExec = vfExec + pbegincodehash = pbegincodehash + nOpCount = nOpCount + +class MaxOpCountError(EvalScriptError): + def __init__(self, **kwargs): + super(MaxOpCountError, self).__init__('max opcode count exceeded',**kwargs) + +class MissingOpArgumentsError(EvalScriptError): + """Missing arguments""" + def __init__(self, opcode, s, n, **kwargs): + super(MissingOpArgumentsError, self).__init__( + 'missing arguments for %s; need %d items, but only %d on stack' % + (OPCODE_NAMES[opcode], n, len(s)), + **kwargs) + +class ArgumentsInvalidError(EvalScriptError): + """Arguments are invalid""" + def __init__(self, opcode, msg, **kwargs): + super(ArgumentsInvalidError, self).__init__( + '%s args invalid: %s' % (OPCODE_NAMES[opcode], msg), + **kwargs) + + +class VerifyOpFailedError(EvalScriptError): + """A VERIFY opcode failed""" + def __init__(self, opcode, **kwargs): + super(VerifyOpFailedError, self).__init__('%s failed' % OPCODE_NAMES[opcode], + **kwargs) + +def _CastToBigNum(s, err_raiser): + v = bitcoin.core._bignum.vch2bn(s) + if len(s) > MAX_NUM_SIZE: + raise err_raiser(EvalScriptError, 'CastToBigNum() : overflow') + return v + +def _CastToBool(s): + for i in range(len(s)): + sv = _bord(s[i]) + if sv != 0: + if (i == (len(s) - 1)) and (sv == 0x80): + return False + return True + + return False + + +def _CheckSig(sig, pubkey, script, txTo, inIdx, err_raiser): + key = bitcoin.core.key.CECKey() + key.set_pubkey(pubkey) + + if len(sig) == 0: + return False + hashtype = _bord(sig[-1]) + sig = sig[:-1] + + # Raw signature hash due to the SIGHASH_SINGLE bug + # + # Note that we never raise an exception if RawSignatureHash() returns an + # error code. However the first error code case, where inIdx >= + # len(txTo.vin), shouldn't ever happen during EvalScript() as that would + # imply the scriptSig being checked doesn't correspond to a valid txout - + # that should cause other validation machinery to fail long before we ever + # got here. + (h, err) = RawSignatureHash(script, txTo, inIdx, hashtype) + return key.verify(h, sig) + + +def _CheckMultiSig(opcode, script, stack, txTo, inIdx, err_raiser, nOpCount): + i = 1 + if len(stack) < i: + err_raiser(MissingOpArgumentsError, opcode, stack, i) + + keys_count = _CastToBigNum(stack[-i], err_raiser) + if keys_count < 0 or keys_count > 20: + err_raiser(ArgumentsInvalidError, opcode, "keys count invalid") + i += 1 + ikey = i + i += keys_count + nOpCount[0] += keys_count + if nOpCount[0] > MAX_SCRIPT_OPCODES: + err_raiser(MaxOpCountError) + if len(stack) < i: + err_raiser(ArgumentsInvalidError, opcode, "not enough keys on stack") + + sigs_count = _CastToBigNum(stack[-i], err_raiser) + if sigs_count < 0 or sigs_count > keys_count: + err_raiser(ArgumentsInvalidError, opcode, "sigs count invalid") + + i += 1 + isig = i + i += sigs_count + if len(stack) < i-1: + raise err_raiser(ArgumentsInvalidError, opcode, "not enough sigs on stack") + elif len(stack) < i: + raise err_raiser(ArgumentsInvalidError, opcode, "missing dummy value") + + # Drop the signature, since there's no way for a signature to sign itself + # + # Of course, this can only come up in very contrived cases now that + # scriptSig and scriptPubKey are processed separately. + for k in range(sigs_count): + sig = stack[-isig - k] + script = FindAndDelete(script, CScript([sig])) + + success = True + + while success and sigs_count > 0: + sig = stack[-isig] + pubkey = stack[-ikey] + + if _CheckSig(sig, pubkey, script, txTo, inIdx, err_raiser): + isig += 1 + sigs_count -= 1 + + ikey += 1 + keys_count -= 1 + + if sigs_count > keys_count: + success = False + + # with VERIFY bail now before we modify the stack + if opcode == OP_CHECKMULTISIGVERIFY: + err_raiser(VerifyOpFailedError, opcode) + + while i > 0: + stack.pop() + i -= 1 + + if opcode == OP_CHECKMULTISIG: + if success: + stack.append(b"\x01") + else: + stack.append(b"\x00") + + +# OP_2MUL and OP_2DIV are *not* included in this list as they are disabled +_ISA_UNOP = { + OP_1ADD, + OP_1SUB, + OP_NEGATE, + OP_ABS, + OP_NOT, + OP_0NOTEQUAL, +} + +def _UnaryOp(opcode, stack, err_raiser): + if len(stack) < 1: + err_raiser(MissingOpArgumentsError, opcode, stack, 1) + bn = _CastToBigNum(stack[-1], err_raiser) + stack.pop() + + if opcode == OP_1ADD: + bn += 1 + + elif opcode == OP_1SUB: + bn -= 1 + + elif opcode == OP_NEGATE: + bn = -bn + + elif opcode == OP_ABS: + if bn < 0: + bn = -bn + + elif opcode == OP_NOT: + bn = long(bn == 0) + + elif opcode == OP_0NOTEQUAL: + bn = long(bn != 0) + + else: + raise AssertionError("Unknown unary opcode encountered; this should not happen") + + stack.append(bitcoin.core._bignum.bn2vch(bn)) + + +# OP_LSHIFT and OP_RSHIFT are *not* included in this list as they are disabled +_ISA_BINOP = { + OP_ADD, + OP_SUB, + OP_BOOLAND, + OP_BOOLOR, + OP_NUMEQUAL, + OP_NUMEQUALVERIFY, + OP_NUMNOTEQUAL, + OP_LESSTHAN, + OP_GREATERTHAN, + OP_LESSTHANOREQUAL, + OP_GREATERTHANOREQUAL, + OP_MIN, + OP_MAX, +} + +def _BinOp(opcode, stack, err_raiser): + if len(stack) < 2: + err_raiser(MissingOpArgumentsError, opcode, stack, 2) + + bn2 = _CastToBigNum(stack[-1], err_raiser) + bn1 = _CastToBigNum(stack[-2], err_raiser) + + # We don't pop the stack yet so that OP_NUMEQUALVERIFY can raise + # VerifyOpFailedError with a correct stack. + + if opcode == OP_ADD: + bn = bn1 + bn2 + + elif opcode == OP_SUB: + bn = bn1 - bn2 + + elif opcode == OP_BOOLAND: + bn = long(bn1 != 0 and bn2 != 0) + + elif opcode == OP_BOOLOR: + bn = long(bn1 != 0 or bn2 != 0) + + elif opcode == OP_NUMEQUAL: + bn = long(bn1 == bn2) + + elif opcode == OP_NUMEQUALVERIFY: + bn = long(bn1 == bn2) + if not bn: + err_raiser(VerifyOpFailedError, opcode) + else: + # No exception, so time to pop the stack + stack.pop() + stack.pop() + return + + elif opcode == OP_NUMNOTEQUAL: + bn = long(bn1 != bn2) + + elif opcode == OP_LESSTHAN: + bn = long(bn1 < bn2) + + elif opcode == OP_GREATERTHAN: + bn = long(bn1 > bn2) + + elif opcode == OP_LESSTHANOREQUAL: + bn = long(bn1 <= bn2) + + elif opcode == OP_GREATERTHANOREQUAL: + bn = long(bn1 >= bn2) + + elif opcode == OP_MIN: + if bn1 < bn2: + bn = bn1 + else: + bn = bn2 + + elif opcode == OP_MAX: + if bn1 > bn2: + bn = bn1 + else: + bn = bn2 + + else: + raise AssertionError("Unknown binop opcode encountered; this should not happen") + + stack.pop() + stack.pop() + stack.append(bitcoin.core._bignum.bn2vch(bn)) + + +def _CheckExec(vfExec): + for b in vfExec: + if not b: + return False + return True + + +def _EvalScript(stack, scriptIn, txTo, inIdx, flags=()): + """Evaluate a script + + """ + if len(scriptIn) > MAX_SCRIPT_SIZE: + raise EvalScriptError('script too large; got %d bytes; maximum %d bytes' % + (len(scriptIn), MAX_SCRIPT_SIZE), + stack=stack, + scriptIn=scriptIn, + txTo=txTo, + inIdx=inIdx, + flags=flags) + + altstack = [] + vfExec = [] + pbegincodehash = 0 + nOpCount = [0] + for (sop, sop_data, sop_pc) in scriptIn.raw_iter(): + fExec = _CheckExec(vfExec) + + def err_raiser(cls, *args): + """Helper function for raising EvalScriptError exceptions + + cls - subclass you want to raise + *args - arguments + + Fills in the state of execution for you. + """ + raise cls(*args, + sop=sop, + sop_data=sop_data, + sop_pc=sop_pc, + stack=stack, scriptIn=scriptIn, txTo=txTo, inIdx=inIdx, flags=flags, + altstack=altstack, vfExec=vfExec, pbegincodehash=pbegincodehash, nOpCount=nOpCount[0]) + + + if sop in DISABLED_OPCODES: + err_raiser(EvalScriptError, 'opcode %s is disabled' % OPCODE_NAMES[sop]) + + if sop > OP_16: + nOpCount[0] += 1 + if nOpCount[0] > MAX_SCRIPT_OPCODES: + err_raiser(MaxOpCountError) + + def check_args(n): + if len(stack) < n: + err_raiser(MissingOpArgumentsError, sop, stack, n) + + + if sop <= OP_PUSHDATA4: + if len(sop_data) > MAX_SCRIPT_ELEMENT_SIZE: + err_raiser(EvalScriptError, + 'PUSHDATA of length %d; maximum allowed is %d' % + (len(sop_data), MAX_SCRIPT_ELEMENT_SIZE)) + + elif fExec: + stack.append(sop_data) + continue + + elif fExec or (OP_IF <= sop <= OP_ENDIF): + + if sop == OP_1NEGATE or ((sop >= OP_1) and (sop <= OP_16)): + v = sop - (OP_1 - 1) + stack.append(bitcoin.core._bignum.bn2vch(v)) + + elif sop in _ISA_BINOP: + _BinOp(sop, stack, err_raiser) + + elif sop in _ISA_UNOP: + _UnaryOp(sop, stack, err_raiser) + + elif sop == OP_2DROP: + check_args(2) + stack.pop() + stack.pop() + + elif sop == OP_2DUP: + check_args(2) + v1 = stack[-2] + v2 = stack[-1] + stack.append(v1) + stack.append(v2) + + elif sop == OP_2OVER: + check_args(4) + v1 = stack[-4] + v2 = stack[-3] + stack.append(v1) + stack.append(v2) + + elif sop == OP_2ROT: + check_args(6) + v1 = stack[-6] + v2 = stack[-5] + del stack[-6] + del stack[-5] + stack.append(v1) + stack.append(v2) + + elif sop == OP_2SWAP: + check_args(4) + tmp = stack[-4] + stack[-4] = stack[-2] + stack[-2] = tmp + + tmp = stack[-3] + stack[-3] = stack[-1] + stack[-1] = tmp + + elif sop == OP_3DUP: + check_args(3) + v1 = stack[-3] + v2 = stack[-2] + v3 = stack[-1] + stack.append(v1) + stack.append(v2) + stack.append(v3) + + elif sop == OP_CHECKMULTISIG or sop == OP_CHECKMULTISIGVERIFY: + tmpScript = CScript(scriptIn[pbegincodehash:]) + _CheckMultiSig(sop, tmpScript, stack, txTo, inIdx, err_raiser, nOpCount) + + elif sop == OP_CHECKSIG or sop == OP_CHECKSIGVERIFY: + check_args(2) + vchPubKey = stack[-1] + vchSig = stack[-2] + tmpScript = CScript(scriptIn[pbegincodehash:]) + + # Drop the signature, since there's no way for a signature to sign itself + # + # Of course, this can only come up in very contrived cases now that + # scriptSig and scriptPubKey are processed separately. + tmpScript = FindAndDelete(tmpScript, CScript([vchSig])) + + ok = _CheckSig(vchSig, vchPubKey, tmpScript, txTo, inIdx, + err_raiser) + if not ok and sop == OP_CHECKSIGVERIFY: + err_raiser(VerifyOpFailedError, sop) + + else: + stack.pop() + stack.pop() + + if ok: + if sop != OP_CHECKSIGVERIFY: + stack.append(b"\x01") + else: + stack.append(b"\x00") + + elif sop == OP_CODESEPARATOR: + pbegincodehash = sop_pc + + elif sop == OP_DEPTH: + bn = len(stack) + stack.append(bitcoin.core._bignum.bn2vch(bn)) + + elif sop == OP_DROP: + check_args(1) + stack.pop() + + elif sop == OP_DUP: + check_args(1) + v = stack[-1] + stack.append(v) + + elif sop == OP_ELSE: + if len(vfExec) == 0: + err_raiser(EvalScriptError, 'ELSE found without prior IF') + vfExec[-1] = not vfExec[-1] + + elif sop == OP_ENDIF: + if len(vfExec) == 0: + err_raiser(EvalScriptError, 'ENDIF found without prior IF') + vfExec.pop() + + elif sop == OP_EQUAL: + check_args(2) + v1 = stack.pop() + v2 = stack.pop() + + if v1 == v2: + stack.append(b"\x01") + else: + stack.append(b"\x00") + + elif sop == OP_EQUALVERIFY: + check_args(2) + v1 = stack[-1] + v2 = stack[-2] + + if v1 == v2: + stack.pop() + stack.pop() + else: + err_raiser(VerifyOpFailedError, sop) + + elif sop == OP_FROMALTSTACK: + if len(altstack) < 1: + err_raiser(MissingOpArgumentsError, sop, altstack, 1) + v = altstack.pop() + stack.append(v) + + elif sop == OP_HASH160: + check_args(1) + stack.append(bitcoin.core.serialize.Hash160(stack.pop())) + + elif sop == OP_HASH256: + check_args(1) + stack.append(bitcoin.core.serialize.Hash(stack.pop())) + + elif sop == OP_IF or sop == OP_NOTIF: + val = False + + if fExec: + check_args(1) + vch = stack.pop() + val = _CastToBool(vch) + if sop == OP_NOTIF: + val = not val + + vfExec.append(val) + + + elif sop == OP_IFDUP: + check_args(1) + vch = stack[-1] + if _CastToBool(vch): + stack.append(vch) + + elif sop == OP_NIP: + check_args(2) + del stack[-2] + + elif sop == OP_NOP or (sop >= OP_NOP1 and sop <= OP_NOP10): + pass + + elif sop == OP_OVER: + check_args(2) + vch = stack[-2] + stack.append(vch) + + elif sop == OP_PICK or sop == OP_ROLL: + check_args(2) + n = _CastToBigNum(stack.pop(), err_raiser) + if n < 0 or n >= len(stack): + err_raiser(EvalScriptError, "Argument for %s out of bounds" % OPCODE_NAMES[sop]) + vch = stack[-n-1] + if sop == OP_ROLL: + del stack[-n-1] + stack.append(vch) + + elif sop == OP_RETURN: + err_raiser(EvalScriptError, "OP_RETURN called") + + elif sop == OP_RIPEMD160: + check_args(1) + + h = hashlib.new('ripemd160') + h.update(stack.pop()) + stack.append(h.digest()) + + elif sop == OP_ROT: + check_args(3) + tmp = stack[-3] + stack[-3] = stack[-2] + stack[-2] = tmp + + tmp = stack[-2] + stack[-2] = stack[-1] + stack[-1] = tmp + + elif sop == OP_SIZE: + check_args(1) + bn = len(stack[-1]) + stack.append(bitcoin.core._bignum.bn2vch(bn)) + + elif sop == OP_SHA1: + check_args(1) + stack.append(hashlib.sha1(stack.pop()).digest()) + + elif sop == OP_SHA256: + check_args(1) + stack.append(hashlib.sha256(stack.pop()).digest()) + + elif sop == OP_SWAP: + check_args(2) + tmp = stack[-2] + stack[-2] = stack[-1] + stack[-1] = tmp + + elif sop == OP_TOALTSTACK: + check_args(1) + v = stack.pop() + altstack.append(v) + + elif sop == OP_TUCK: + check_args(2) + vch = stack[-1] + stack.insert(len(stack) - 2, vch) + + elif sop == OP_VERIFY: + check_args(1) + v = _CastToBool(stack[-1]) + if v: + stack.pop() + else: + raise err_raiser(VerifyOpFailedError, sop) + + elif sop == OP_WITHIN: + check_args(3) + bn3 = _CastToBigNum(stack[-1], err_raiser) + bn2 = _CastToBigNum(stack[-2], err_raiser) + bn1 = _CastToBigNum(stack[-3], err_raiser) + stack.pop() + stack.pop() + stack.pop() + v = (bn2 <= bn1) and (bn1 < bn3) + if v: + stack.append(b"\x01") + else: + stack.append(b"\x00") + + else: + err_raiser(EvalScriptError, 'unsupported opcode 0x%x' % sop) + + # size limits + if len(stack) + len(altstack) > MAX_STACK_ITEMS: + err_raiser(EvalScriptError, 'max stack items limit reached') + + # Unterminated IF/NOTIF/ELSE block + if len(vfExec): + raise EvalScriptError('Unterminated IF/ELSE block', + stack=stack, + scriptIn=scriptIn, + txTo=txTo, + inIdx=inIdx, + flags=flags) + + +def EvalScript(stack, scriptIn, txTo, inIdx, flags=()): + """Evaluate a script + + stack - Initial stack + scriptIn - Script + txTo - Transaction the script is a part of + inIdx - txin index of the scriptSig + flags - SCRIPT_VERIFY_* flags to apply + """ + + try: + _EvalScript(stack, scriptIn, txTo, inIdx, flags=flags) + except CScriptInvalidError as err: + raise EvalScriptError(repr(err), + stack=stack, + scriptIn=scriptIn, + txTo=txTo, + inIdx=inIdx, + flags=flags) + +class VerifyScriptError(bitcoin.core.ValidationError): + pass + +def VerifyScript(scriptSig, scriptPubKey, txTo, inIdx, flags=()): + """Verify a scriptSig satisfies a scriptPubKey + + scriptSig - Signature + scriptPubKey - PubKey + txTo - Spending transaction + inIdx - Index of the transaction input containing scriptSig + + Raises a ValidationError subclass if the validation fails. + """ + stack = [] + EvalScript(stack, scriptSig, txTo, inIdx, flags=flags) + if SCRIPT_VERIFY_P2SH in flags: + stackCopy = list(stack) + EvalScript(stack, scriptPubKey, txTo, inIdx, flags=flags) + if len(stack) == 0: + raise VerifyScriptError("scriptPubKey left an empty stack") + if not _CastToBool(stack[-1]): + raise VerifyScriptError("scriptPubKey returned false") + + # Additional validation for spend-to-script-hash transactions + if SCRIPT_VERIFY_P2SH in flags and scriptPubKey.is_p2sh(): + if not scriptSig.is_push_only(): + raise VerifyScriptError("P2SH scriptSig not is_push_only()") + + # stackCopy cannot be empty here, because if it was the + # P2SH HASH <> EQUAL scriptPubKey would be evaluated with + # an empty stack and the EvalScript above would return false. + assert len(stackCopy) + + pubKey2 = CScript(stackCopy.pop()) + + EvalScript(stackCopy, pubKey2, txTo, inIdx, flags=flags) + + if not len(stackCopy): + raise VerifyScriptError("P2SH inner scriptPubKey left an empty stack") + + if not _CastToBool(stackCopy[-1]): + raise VerifyScriptError("P2SH inner scriptPubKey returned false") + + +class VerifySignatureError(bitcoin.core.ValidationError): + pass + +def VerifySignature(txFrom, txTo, inIdx): + """Verify a scriptSig signature can spend a txout + + Verifies that the scriptSig in txTo.vin[inIdx] is a valid scriptSig for the + corresponding COutPoint in transaction txFrom. + """ + if inIdx < 0: + raise VerifySignatureError("inIdx negative") + if inIdx >= len(txTo.vin): + raise VerifySignatureError("inIdx >= len(txTo.vin)") + txin = txTo.vin[inIdx] + + if txin.prevout.n < 0: + raise VerifySignatureError("txin prevout.n negative") + if txin.prevout.n >= len(txFrom.vout): + raise VerifySignatureError("txin prevout.n >= len(txFrom.vout)") + txout = txFrom.vout[txin.prevout.n] + + if txin.prevout.hash != txFrom.GetHash(): + raise VerifySignatureError("prevout hash does not match txFrom") + + VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, inIdx) + + +__all__ = ( + 'MAX_STACK_ITEMS', + 'SCRIPT_VERIFY_P2SH', + 'SCRIPT_VERIFY_STRICTENC', + 'SCRIPT_VERIFY_EVEN_S', + 'SCRIPT_VERIFY_NOCACHE', + 'EvalScriptError', + 'MaxOpCountError', + 'MissingOpArgumentsError', + 'ArgumentsInvalidError', + 'VerifyOpFailedError', + 'EvalScript', + 'VerifyScriptError', + 'VerifyScript', + 'VerifySignatureError', + 'VerifySignature', +) diff --git a/bitcoin/core/serialize.py b/bitcoin/core/serialize.py new file mode 100644 index 0000000..8192ea3 --- /dev/null +++ b/bitcoin/core/serialize.py @@ -0,0 +1,365 @@ +# Copyright (C) 2012-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Serialization routines + +You probabably don't need to use these directly. +""" + +from __future__ import absolute_import, division, print_function, unicode_literals + +import hashlib +import struct + +# Py3 compatibility +import sys + +if sys.version > '3': + _bchr = lambda x: bytes([x]) + _bord = lambda x: x[0] + from io import BytesIO as _BytesIO +else: + _bchr = chr + _bord = ord + from cStringIO import StringIO as _BytesIO + +MAX_SIZE = 0x02000000 + + +def Hash(msg): + """SHA256^2)(msg) -> bytes""" + return hashlib.sha256(hashlib.sha256(msg).digest()).digest() + +def Hash160(msg): + """RIPEME160(SHA256(msg)) -> bytes""" + h = hashlib.new('ripemd160') + h.update(hashlib.sha256(msg).digest()) + return h.digest() + + +class SerializationError(Exception): + """Base class for serialization errors""" + + +class SerializationTruncationError(SerializationError): + """Serialized data was truncated + + Thrown by deserialize() and stream_deserialize() + """ + +class DeserializationExtraDataError(SerializationError): + """Deserialized data had extra data at the end + + Thrown by deserialize() when not all data is consumed during + deserialization. The deserialized object and extra padding not consumed are + saved. + """ + def __init__(self, msg, obj, padding): + super(DeserializationExtraDataError, self).__init__(msg) + self.obj = obj + self.padding = padding + +def ser_read(f, n): + """Read from a stream safely + + Raises SerializationError and SerializationTruncationError appropriately. + Use this instead of f.read() in your classes stream_(de)serialization() + functions. + """ + if n > MAX_SIZE: + raise SerializationError('Asked to read 0x%x bytes; MAX_SIZE exceeded' % n) + r = f.read(n) + if len(r) < n: + raise SerializationTruncationError('Asked to read %i bytes, but only got %i' % (n, len(r))) + return r + + +class Serializable(object): + """Base class for serializable objects""" + + __slots__ = [] + + def stream_serialize(self, f): + """Serialize to a stream""" + raise NotImplementedError + + @classmethod + def stream_deserialize(cls, f): + """Deserialize from a stream""" + raise NotImplementedError + + def serialize(self): + """Serialize, returning bytes""" + f = _BytesIO() + self.stream_serialize(f) + return f.getvalue() + + @classmethod + def deserialize(cls, buf, allow_padding=False): + """Deserialize bytes, returning an instance + + allow_padding - Allow buf to include extra padding. (default False) + + If allow_padding is False and not all bytes are consumed during + deserialization DeserializationExtraDataError will be raised. + """ + fd = _BytesIO(buf) + r = cls.stream_deserialize(fd) + if not allow_padding: + padding = fd.read() + if len(padding) != 0: + raise DeserializationExtraDataError('Not all bytes consumed during deserialization', + r, padding) + return r + + def GetHash(self): + """Return the hash of the serialized object""" + return Hash(self.serialize()) + + def __eq__(self, other): + if (not isinstance(other, self.__class__) and + not isinstance(self, other.__class__)): + return NotImplemented + return self.serialize() == other.serialize() + + def __ne__(self, other): + return not (self == other) + + def __hash__(self): + return hash(self.serialize()) + +class ImmutableSerializable(Serializable): + """Immutable serializable object""" + + __slots__ = ['_cached_GetHash', '_cached__hash__'] + + def __setattr__(self, name, value): + raise AttributeError('Object is immutable') + + def __delattr__(self, name): + raise AttributeError('Object is immutable') + + def GetHash(self): + """Return the hash of the serialized object""" + try: + return self._cached_GetHash + except AttributeError: + _cached_GetHash = super(ImmutableSerializable, self).GetHash() + object.__setattr__(self, '_cached_GetHash', _cached_GetHash) + return _cached_GetHash + + def __hash__(self): + try: + return self._cached__hash__ + except AttributeError: + _cached__hash__ = hash(self.serialize()) + object.__setattr__(self, '_cached__hash__', _cached__hash__) + return _cached__hash__ + +class Serializer(object): + """Base class for object serializers""" + def __new__(cls): + raise NotImplementedError + + @classmethod + def stream_serialize(cls, obj, f): + raise NotImplementedError + @classmethod + def stream_deserialize(cls, f): + raise NotImplementedError + + @classmethod + def serialize(cls, obj): + f = _BytesIO() + cls.stream_serialize(obj, f) + return f.getvalue() + + @classmethod + def deserialize(cls, buf): + return cls.stream_deserialize(_BytesIO(buf)) + + +class VarIntSerializer(Serializer): + """Serialization of variable length ints""" + @classmethod + def stream_serialize(cls, i, f): + if i < 0: + raise ValueError('varint must be non-negative integer') + elif i < 0xfd: + f.write(_bchr(i)) + elif i <= 0xffff: + f.write(_bchr(0xfd)) + f.write(struct.pack(b'> 24) & 0xFF + if nbytes <= 3: + v = (c & 0xFFFFFF) >> 8 * (3 - nbytes) + else: + v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) + return v + +def compact_from_uint256(v): + """Convert uint256 to compact encoding + """ + nbytes = (v.bit_length() + 7) >> 3 + compact = 0 + if nbytes <= 3: + compact = (v & 0xFFFFFF) << 8 * (3 - nbytes) + else: + compact = v >> 8 * (nbytes - 3) + compact = compact & 0xFFFFFF + + # If the sign bit (0x00800000) is set, divide the mantissa by 256 and + # increase the exponent to get an encoding without it set. + if compact & 0x00800000: + compact >>= 8 + nbytes += 1 + + return compact | nbytes << 24 + +def uint256_to_shortstr(u): + s = "%064x" % (u,) + return s[:16] + + +__all__ = ( + 'MAX_SIZE', + 'Hash', + 'Hash160', + 'SerializationError', + 'SerializationTruncationError', + 'DeserializationExtraDataError', + 'ser_read', + 'Serializable', + 'ImmutableSerializable', + 'Serializer', + 'VarIntSerializer', + 'BytesSerializer', + 'VectorSerializer', + 'uint256VectorSerializer', + 'intVectorSerialzer', + 'VarStringSerializer', + 'uint256_from_str', + 'uint256_from_compact', + 'compact_from_uint256', + 'uint256_to_shortstr', +) diff --git a/bitcoin/coredefs.py b/bitcoin/coredefs.py deleted file mode 100644 index 5ecf8eb..0000000 --- a/bitcoin/coredefs.py +++ /dev/null @@ -1,62 +0,0 @@ - -# -# coredefs.py -# -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -from __future__ import absolute_import, division, print_function, unicode_literals - -PROTO_VERSION = 60002 - -CADDR_TIME_VERSION = 31402 - -MIN_PROTO_VERSION = 209 - -BIP0031_VERSION = 60000 - -NOBLKS_VERSION_START = 32000 -NOBLKS_VERSION_END = 32400 - -MEMPOOL_GD_VERSION = 60002 - -COIN = 100000000 -MAX_MONEY = 21000000 * COIN - -def MoneyRange(nValue): - return 0<= nValue <= MAX_MONEY - -class NetMagic(object): - def __init__(self, msg_start, block0, checkpoints): - self.msg_start = msg_start - self.block0 = block0 - self.checkpoints = checkpoints - - self.checkpoint_max = 0 - for height in self.checkpoints.keys(): - if height > self.checkpoint_max: - self.checkpoint_max = height - -NETWORKS = { - 'mainnet' : NetMagic(b"\xf9\xbe\xb4\xd9", - 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f, - { - 0: 0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f, - 11111: 0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d, - 33333: 0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6, - 74000: 0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20, - 105000: 0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97, - 134444: 0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe, - 168000: 0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763, - 193000: 0x000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317, - 210000: 0x000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e, - 216116: 0x00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e, - }), - 'testnet3' : NetMagic(b"\x0b\x11\x09\x07", - 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943, - { - 0: 0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943, - }) -} - diff --git a/bitcoin/hash.py b/bitcoin/hash.py deleted file mode 100644 index 158e61a..0000000 --- a/bitcoin/hash.py +++ /dev/null @@ -1,78 +0,0 @@ - -# -# hash.py -# -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -from __future__ import absolute_import, division, print_function, unicode_literals - -import struct -from bitcoin.serialize import * -from bitcoin.coredefs import * -from bitcoin.script import CScript - -def ROTL32(x, r): - assert x <= 0xFFFFFFFF - return ((x << r) & 0xFFFFFFFF) | (x >> (32 - r)) - -def MurmurHash3(nHashSeed, vDataToHash): - """MurmurHash3 (x86_32) - - Used for bloom filters. See http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - """ - - assert nHashSeed <= 0xFFFFFFFF - - h1 = nHashSeed - c1 = 0xcc9e2d51 - c2 = 0x1b873593 - - # body - i = 0 - while i < len(vDataToHash) - len(vDataToHash) % 4 \ - and len(vDataToHash) - i >= 4: - - k1 = struct.unpack(b" '3': - # In Py3 indexing bytes returns numbers, not characters - bord = lambda x: x - if len(vDataToHash) & 3 >= 3: - k1 ^= bord(vDataToHash[j+2]) << 16 - if len(vDataToHash) & 3 >= 2: - k1 ^= bord(vDataToHash[j+1]) << 8 - if len(vDataToHash) & 3 >= 1: - k1 ^= bord(vDataToHash[j]) - - k1 &= 0xFFFFFFFF - k1 = (k1 * c1) & 0xFFFFFFFF - k1 = ROTL32(k1, 15) - k1 = (k1 * c2) & 0xFFFFFFFF - h1 ^= k1 - - # finalization - h1 ^= len(vDataToHash) & 0xFFFFFFFF - h1 ^= (h1 & 0xFFFFFFFF) >> 16 - h1 *= 0x85ebca6b - h1 ^= (h1 & 0xFFFFFFFF) >> 13 - h1 *= 0xc2b2ae35 - h1 ^= (h1 & 0xFFFFFFFF) >> 16 - - return h1 & 0xFFFFFFFF diff --git a/bitcoin/key.py b/bitcoin/key.py deleted file mode 100644 index 28fc9de..0000000 --- a/bitcoin/key.py +++ /dev/null @@ -1,119 +0,0 @@ - -# -# key.py - OpenSSL wrapper -# Source: git://github.com/joric/brutus.git -# which was forked from git://github.com/samrushing/caesure.git -# - -import ctypes -import ctypes.util - -ssl = ctypes.cdll.LoadLibrary (ctypes.util.find_library ('ssl') or 'libeay32') - -# this specifies the curve used with ECDSA. -NID_secp256k1 = 714 # from openssl/obj_mac.h - -# Thx to Sam Devlin for the ctypes magic 64-bit fix. -def check_result (val, func, args): - if val == 0: - raise ValueError - else: - return ctypes.c_void_p (val) - -ssl.EC_KEY_new_by_curve_name.restype = ctypes.c_void_p -ssl.EC_KEY_new_by_curve_name.errcheck = check_result - -class CKey: - - def __init__(self): - self.POINT_CONVERSION_COMPRESSED = 2 - self.POINT_CONVERSION_UNCOMPRESSED = 4 - self.k = ssl.EC_KEY_new_by_curve_name(NID_secp256k1) - - def __del__(self): - if ssl: - ssl.EC_KEY_free(self.k) - self.k = None - - def generate(self, secret=None): - if secret: - self.prikey = secret - priv_key = ssl.BN_bin2bn(secret, 32, ssl.BN_new()) - group = ssl.EC_KEY_get0_group(self.k) - pub_key = ssl.EC_POINT_new(group) - ctx = ssl.BN_CTX_new() - if not ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx): - raise ValueError("Could not derive public key from the supplied secret.") - ssl.EC_POINT_mul(group, pub_key, priv_key, None, None, ctx) - ssl.EC_KEY_set_private_key(self.k, priv_key) - ssl.EC_KEY_set_public_key(self.k, pub_key) - ssl.EC_POINT_free(pub_key) - ssl.BN_CTX_free(ctx) - return self.k - else: - return ssl.EC_KEY_generate_key(self.k) - - def set_privkey(self, key): - self.mb = ctypes.create_string_buffer(key) - ssl.d2i_ECPrivateKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key)) - - def set_pubkey(self, key): - self.mb = ctypes.create_string_buffer(key) - ssl.o2i_ECPublicKey(ctypes.byref(self.k), ctypes.byref(ctypes.pointer(self.mb)), len(key)) - - def get_privkey(self): - size = ssl.i2d_ECPrivateKey(self.k, 0) - mb_pri = ctypes.create_string_buffer(size) - ssl.i2d_ECPrivateKey(self.k, ctypes.byref(ctypes.pointer(mb_pri))) - return mb_pri.raw - - def get_pubkey(self): - size = ssl.i2o_ECPublicKey(self.k, 0) - mb = ctypes.create_string_buffer(size) - ssl.i2o_ECPublicKey(self.k, ctypes.byref(ctypes.pointer(mb))) - return mb.raw - - def sign(self, hash): - sig_size0 = ctypes.c_uint32() - sig_size0.value = ssl.ECDSA_size(self.k) - mb_sig = ctypes.create_string_buffer(sig_size0.value) - result = ssl.ECDSA_sign(0, hash, len(hash), mb_sig, ctypes.byref(sig_size0), self.k) - assert 1 == result - return mb_sig.raw[:sig_size0.value] - - def verify(self, hash, sig): - return ssl.ECDSA_verify(0, hash, len(hash), sig, len(sig), self.k) == 1 - - def set_compressed(self, compressed): - if compressed: - form = self.POINT_CONVERSION_COMPRESSED - else: - form = self.POINT_CONVERSION_UNCOMPRESSED - ssl.EC_KEY_set_conv_form(self.k, form) - -if __name__ == '__main__': - # ethalone keys - ec_secret = '' + \ - 'a0dc65ffca799873cbea0ac274015b9526505daaaed385155425f7337704883e' - ec_private = '308201130201010420' + \ - 'a0dc65ffca799873cbea0ac274015b9526505daaaed385155425f7337704883e' + \ - 'a081a53081a2020101302c06072a8648ce3d0101022100' + \ - 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' + \ - '300604010004010704410479be667ef9dcbbac55a06295ce870b07029bfcdb2d' + \ - 'ce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a6' + \ - '8554199c47d08ffb10d4b8022100' + \ - 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' + \ - '020101a14403420004' + \ - '0791dc70b75aa995213244ad3f4886d74d61ccd3ef658243fcad14c9ccee2b0a' + \ - 'a762fbc6ac0921b8f17025bb8458b92794ae87a133894d70d7995fc0b6b5ab90' - - k = CKey() - k.generate (ec_secret.decode('hex')) - k.set_compressed(True) - print(k.get_privkey ().encode('hex')) - print(k.get_pubkey().encode('hex')) - # not sure this is needed any more: print k.get_secret().encode('hex') - - hash = 'Hello, world!' - print(k.verify(hash, k.sign(hash))) - diff --git a/bitcoin/messages.py b/bitcoin/messages.py index 2b35569..29ee77c 100644 --- a/bitcoin/messages.py +++ b/bitcoin/messages.py @@ -1,333 +1,569 @@ - +# Copyright (C) 2012-2015 The python-bitcoinlib developers # -# messages.py +# This file is part of python-bitcoinlib. # -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. # +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. from __future__ import absolute_import, division, print_function, unicode_literals +import hashlib +import random import struct import time -import random -import cStringIO -from bitcoin.coredefs import * + +# Py3 compatibility +import sys + +if sys.version > '3': + _bchr = lambda x: bytes([x]) + _bord = lambda x: x[0] + from io import BytesIO as _BytesIO +else: + _bchr = chr + _bord = ord + from cStringIO import StringIO as _BytesIO + +# Bad practice, so we have a __all__ at the end; this should be cleaned up +# later. from bitcoin.core import * +from bitcoin.core.serialize import * +from bitcoin.net import * +import bitcoin MSG_TX = 1 MSG_BLOCK = 2 +MSG_FILTERED_BLOCK = 3 + +REJECT_MALFORMED = 0x01 +REJECT_INVALID = 0x10 +REJECT_OBSOLETE = 0x11 +REJECT_DUPLICATE = 0x12 +REJECT_NONSTANDARD = 0x40 +REJECT_DUST = 0x41 +REJECT_INSUFFICIENTFEE = 0x42 +REJECT_CHECKPOINT = 0x43 -class msg_version(object): + +class MsgSerializable(Serializable): + def __init__(self, protover=PROTO_VERSION): + self.protover = protover + + def msg_ser(self, f): + raise NotImplementedError + + @classmethod + def msg_deser(cls, f, protover=PROTO_VERSION): + raise NotImplementedError + + def to_bytes(self): + f = _BytesIO() + self.msg_ser(f) + body = f.getvalue() + res = bitcoin.params.MESSAGE_START + res += self.command + res += b"\x00" * (12 - len(self.command)) + res += struct.pack(b"= 106: - self.addrFrom = CAddress(MIN_PROTO_VERSION) - self.addrFrom.deserialize(f) - self.nNonce = struct.unpack(b"= 209: - self.nStartingHeight = struct.unpack(b"= 106: + c.addrFrom = CAddress.stream_deserialize(f, True) + c.nNonce = struct.unpack(b"= 209: + c.nStartingHeight = struct.unpack(b" BIP0031_VERSION: - self.nonce = struct.unpack(b" BIP0031_VERSION: - r += struct.pack(b"= CADDR_TIME_VERSION and not without_time: + c.nTime = struct.unpack(b"H", ser_read(f, 2))[0] + return c + + + def stream_serialize(self, f, without_time=False): + if self.protover >= CADDR_TIME_VERSION and not without_time: + f.write(struct.pack(b"H", self.port)) + + + + + def __repr__(self): + return "CAddress(nTime=%d nServices=%i ip=%s port=%i)" % (self.nTime, self.nServices, self.ip, self.port) + + +class CInv(Serializable): + typemap = { + 0: "Error", + 1: "TX", + 2: "Block", + 3: "FilteredBlock"} + + def __init__(self): + self.type = 0 + self.hash = 0 + + @classmethod + def stream_deserialize(cls, f): + c = cls() + c.type = struct.unpack(b" '3': + unhexlify = lambda h: binascii.unhexlify(h.encode('utf8')) + hexlify = lambda b: binascii.hexlify(b).decode('utf8') class JSONRPCException(Exception): @@ -63,12 +55,10 @@ def __init__(self, rpc_error): class RawProxy(object): - # FIXME: need a CChainParams rather than hard-coded service_port def __init__(self, service_url=None, - service_port=8332, + service_port=None, btc_conf_file=None, - timeout=HTTP_TIMEOUT, - _connection=None): + timeout=DEFAULT_HTTP_TIMEOUT): """Low-level JSON-RPC proxy Unlike Proxy no conversion is done from the raw JSON objects. @@ -78,16 +68,17 @@ def __init__(self, service_url=None, # Figure out the path to the bitcoin.conf file if btc_conf_file is None: if platform.system() == 'Darwin': - btc_conf_file = os.path.join(os.environ['APPDATA'], 'Bitcoin') - elif platform.system() == 'Windows': btc_conf_file = os.path.expanduser('~/Library/Application Support/Bitcoin/') + elif platform.system() == 'Windows': + btc_conf_file = os.path.join(os.environ['APPDATA'], 'Bitcoin') else: btc_conf_file = os.path.expanduser('~/.bitcoin') btc_conf_file = os.path.join(btc_conf_file, 'bitcoin.conf') # Extract contents of bitcoin.conf to build service_url with open(btc_conf_file, 'r') as fd: - conf = {} + # Bitcoin Core accepts empty rpcuser, not specified in btc_conf_file + conf = {'rpcuser': ""} for line in fd.readlines(): if '#' in line: line = line[:line.index('#')] @@ -96,8 +87,11 @@ def __init__(self, service_url=None, k, v = line.split('=', 1) conf[k.strip()] = v.strip() + if service_port is None: + service_port = bitcoin.params.RPC_PORT conf['rpcport'] = int(conf.get('rpcport', service_port)) conf['rpcssl'] = conf.get('rpcssl', '0') + conf['rpchost'] = conf.get('rpcconnect', 'localhost') if conf['rpcssl'].lower() in ('0', 'false'): conf['rpcssl'] = False @@ -106,15 +100,25 @@ def __init__(self, service_url=None, else: raise ValueError('Unknown rpcssl value %r' % conf['rpcssl']) - service_url = ('%s://%s:%s@localhost:%d' % + if 'rpcpassword' not in conf: + raise ValueError('The value of rpcpassword not specified in the configuration file: %s' % btc_conf_file) + + service_url = ('%s://%s:%s@%s:%d' % ('https' if conf['rpcssl'] else 'http', conf['rpcuser'], conf['rpcpassword'], - conf['rpcport'])) + conf['rpchost'], conf['rpcport'])) self.__service_url = service_url self.__url = urlparse.urlparse(service_url) + + if self.__url.scheme not in ('https', 'http'): + raise ValueError('Unsupported URL scheme %r' % self.__url.scheme) + if self.__url.port is None: - port = 80 + if self.__url.scheme == 'https': + port = httplib.HTTPS_PORT + else: + port = httplib.HTTP_PORT else: port = self.__url.port self.__id_count = 0 @@ -122,16 +126,13 @@ def __init__(self, service_url=None, authpair = authpair.encode('utf8') self.__auth_header = b"Basic " + base64.b64encode(authpair) - if _connection: - # Callables re-use the connection of the original proxy - self.__conn = _connection - elif self.__url.scheme == 'https': - self.__conn = httplib.HTTPSConnection(self.__url.hostname, port, - None, None, False, - timeout) + if self.__url.scheme == 'https': + self.__conn = httplib.HTTPSConnection(self.__url.hostname, port=port, + key_file=None, cert_file=None, + timeout=timeout) else: - self.__conn = httplib.HTTPConnection(self.__url.hostname, port, - False, timeout) + self.__conn = httplib.HTTPConnection(self.__url.hostname, port=port, + timeout=timeout) def _call(self, service_name, *args): @@ -143,7 +144,7 @@ def _call(self, service_name, *args): 'id': self.__id_count}) self.__conn.request('POST', self.__url.path, postdata, {'Host': self.__url.hostname, - 'User-Agent': USER_AGENT, + 'User-Agent': DEFAULT_USER_AGENT, 'Authorization': self.__auth_header, 'Content-type': 'application/json'}) @@ -175,7 +176,7 @@ def _batch(self, rpc_call_list): postdata = json.dumps(list(rpc_call_list)) self.__conn.request('POST', self.__url.path, postdata, {'Host': self.__url.hostname, - 'User-Agent': USER_AGENT, + 'User-Agent': DEFAULT_USER_AGENT, 'Authorization': self.__auth_header, 'Content-type': 'application/json'}) @@ -190,21 +191,27 @@ def _get_response(self): return json.loads(http_response.read().decode('utf8'), parse_float=decimal.Decimal) + def __del__(self): + self.__conn.close() + class Proxy(RawProxy): def __init__(self, service_url=None, - service_port=8332, + service_port=None, btc_conf_file=None, - timeout=HTTP_TIMEOUT, + timeout=DEFAULT_HTTP_TIMEOUT, **kwargs): """Create a proxy to a bitcoin RPC service Unlike RawProxy data is passed as objects, rather than JSON. (not yet - fully implemented) + fully implemented) Assumes Bitcoin Core version >= 0.9; older versions + mostly work, but there are a few incompatibilities. If service_url is not specified the username and password are read out of the file btc_conf_file. If btc_conf_file is not specified - ~/.bitcoin/bitcoin.conf or equivalent is used by default. + ~/.bitcoin/bitcoin.conf or equivalent is used by default. The default + port is set according to the chain parameters in use: mainnet, testnet, + or regtest. Usually no arguments to Proxy() are needed; the local bitcoind will be used. @@ -212,13 +219,63 @@ def __init__(self, service_url=None, timeout - timeout in seconds before the HTTP interface times out """ super(Proxy, self).__init__(service_url=service_url, service_port=service_port, btc_conf_file=btc_conf_file, - timeout=HTTP_TIMEOUT, + timeout=timeout, **kwargs) + def dumpprivkey(self, addr): + """Return the private key matching an address + """ + r = self._call('dumpprivkey', str(addr)) + + return CBitcoinSecret(r) + + def getaccountaddress(self, account=None): + """Return the current Bitcoin address for receiving payments to this account.""" + r = self._call('getaccountaddress', account) + return CBitcoinAddress(r) + + def getbalance(self, account='*', minconf=1): + """Get the balance + + account - The selected account. Defaults to "*" for entire wallet. It may be the default account using "". + minconf - Only include transactions confirmed at least this many times. (default=1) + """ + r = self._call('getbalance', account, minconf) + return int(r*COIN) + + def getblock(self, block_hash): + """Get block + + Raises IndexError if block_hash is not valid. + """ + try: + block_hash = b2lx(block_hash) + except TypeError: + raise TypeError('%s.getblock(): block_hash must be bytes; got %r instance' % + (self.__class__.__name__, block_hash.__class__)) + try: + r = self._call('getblock', block_hash, False) + except JSONRPCException as ex: + raise IndexError('%s.getblock(): %s (%d)' % + (self.__class__.__name__, ex.error['message'], ex.error['code'])) + return CBlock.deserialize(unhexlify(r)) + + def getblockhash(self, height): + """Return hash of block in best-block-chain at height. + + Raises IndexError if height is not valid. + """ + try: + return lx(self._call('getblockhash', height)) + except JSONRPCException as ex: + raise IndexError('%s.getblockhash(): %s (%d)' % + (self.__class__.__name__, ex.error['message'], ex.error['code'])) + def getinfo(self): - """Returns an object containing various state info""" + """Return an object containing various state info""" r = self._call('getinfo') - r['balance'] = int(r['balance'] * COIN) + if 'balance' in r: + r['balance'] = int(r['balance'] * COIN) r['paytxfee'] = int(r['paytxfee'] * COIN) return r @@ -234,15 +291,215 @@ def getnewaddress(self, account=None): else: r = self._call('getnewaddress') - return CBitcoinAddress.from_str(r) + return CBitcoinAddress(r) - def getaccountaddress(self, account=None): - """Return the current Bitcoin address for receiving payments to this account.""" - r = self._call('getaccountaddress', account) - return CBitcoinAddress.from_str(r) + def getrawchangeaddress(self): + """Returns a new Bitcoin address, for receiving change. + + This is for use with raw transactions, NOT normal use. + """ + r = self._call('getrawchangeaddress') + return CBitcoinAddress(r) + + def getrawmempool(self, verbose=False): + """Return the mempool""" + if verbose: + return self._call('getrawmempool', verbose) + + else: + r = self._call('getrawmempool') + r = [lx(txid) for txid in r] + return r + + def getrawtransaction(self, txid, verbose=False): + """Return transaction with hash txid + + Raises IndexError if transaction not found. + + verbose - If true a dict is returned instead with additional information + on the transaction. + + Note that if all txouts are spent and the transaction index is not + enabled the transaction may not be available. + """ + try: + r = self._call('getrawtransaction', b2lx(txid), 1 if verbose else 0) + except JSONRPCException as ex: + raise IndexError('%s.getrawtransaction(): %s (%d)' % + (self.__class__.__name__, ex.error['message'], ex.error['code'])) + if verbose: + r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) + del r['hex'] + del r['txid'] + del r['version'] + del r['locktime'] + del r['vin'] + del r['vout'] + r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None + else: + r = CTransaction.deserialize(unhexlify(r)) + + return r + + def getreceivedbyaddress(self, addr, minconf=1): + """Return total amount received by given a (wallet) address + + Get the amount received by
in transactions with at least + [minconf] confirmations. + + Works only for addresses in the local wallet; other addresses will + always show zero. + + addr - The address. (CBitcoinAddress instance) + minconf - Only include transactions confirmed at least this many times. (default=1) + """ + r = self._call('getreceivedbyaddress', str(addr), minconf) + return int(r * COIN) + + def gettransaction(self, txid): + """Get detailed information about in-wallet transaction txid + + Raises IndexError if transaction not found in the wallet. + + FIXME: Returned data types are not yet converted. + """ + try: + r = self._call('gettransaction', b2lx(txid)) + except JSONRPCException as ex: + raise IndexError('%s.getrawtransaction(): %s (%d)' % + (self.__class__.__name__, ex.error['message'], ex.error['code'])) + return r + + def gettxout(self, outpoint, includemempool=True): + """Return details about an unspent transaction output. + + Raises IndexError if outpoint is not found or was spent. + + includemempool - Include mempool txouts + """ + r = self._call('gettxout', b2lx(outpoint.hash), outpoint.n, includemempool) + + if r is None: + raise IndexError('%s.gettxout(): unspent txout %r not found' % (self.__class__.__name__, outpoint)) + + r['txout'] = CTxOut(int(r['value'] * COIN), + CScript(unhexlify(r['scriptPubKey']['hex']))) + del r['value'] + del r['scriptPubKey'] + r['bestblock'] = lx(r['bestblock']) + return r + + def importaddress(self, addr, label='', rescan=True): + """Adds an address or pubkey to wallet without the associated privkey.""" + addr = str(addr) + + r = self._call('importaddress', addr, label, rescan) + return r + + def listunspent(self, minconf=0, maxconf=9999999, addrs=None): + """Return unspent transaction outputs in wallet + + Outputs will have between minconf and maxconf (inclusive) + confirmations, optionally filtered to only include txouts paid to + addresses in addrs. + """ + r = None + if addrs is None: + r = self._call('listunspent', minconf, maxconf) + else: + addrs = [str(addr) for addr in addrs] + r = self._call('listunspent', minconf, maxconf, addrs) + + r2 = [] + for unspent in r: + unspent['outpoint'] = COutPoint(lx(unspent['txid']), unspent['vout']) + del unspent['txid'] + del unspent['vout'] + + unspent['address'] = CBitcoinAddress(unspent['address']) + unspent['scriptPubKey'] = CScript(unhexlify(unspent['scriptPubKey'])) + unspent['amount'] = int(unspent['amount'] * COIN) + r2.append(unspent) + return r2 + + def lockunspent(self, unlock, outpoints): + """Lock or unlock outpoints""" + json_outpoints = [{'txid':b2lx(outpoint.hash),'vout':outpoint.n} for outpoint in outpoints] + return self._call('lockunspent', unlock, json_outpoints) + + def sendrawtransaction(self, tx, allowhighfees=False): + """Submit transaction to local node and network. + + allowhighfees - Allow even if fees are unreasonably high. + """ + hextx = hexlify(tx.serialize()) + r = None + if allowhighfees: + r = self._call('sendrawtransaction', hextx, True) + else: + r = self._call('sendrawtransaction', hextx) + return lx(r) + + def sendmany(self, fromaccount, payments, minconf=1, comment=''): + """Sent amount to a given address""" + json_payments = {str(addr):float(amount)/COIN for addr,amount in payments.items()} + r = self._call('sendmany', fromaccount, json_payments, minconf, comment) + return lx(r) + + def sendtoaddress(self, addr, amount): + """Sent amount to a given address""" + addr = str(addr) + amount = float(amount)/COIN + r = self._call('sendtoaddress', addr, amount) + return lx(r) + + def signrawtransaction(self, tx, *args): + """Sign inputs for transaction + + FIXME: implement options + """ + hextx = hexlify(tx.serialize()) + r = self._call('signrawtransaction', hextx, *args) + r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) + del r['hex'] + return r + + def submitblock(self, block, params=None): + """Submit a new block to the network. + + params is optional and is currently ignored by bitcoind. See + https://en.bitcoin.it/wiki/BIP_0022 for full specification. + """ + hexblock = hexlify(block.serialize()) + if params is not None: + return self._call('submitblock', hexblock, params) + else: + return self._call('submitblock', hexblock) def validateaddress(self, address): """Return information about an address""" r = self._call('validateaddress', str(address)) - r['address'] = CBitcoinAddress.from_str(r['address']) + if r['isvalid']: + r['address'] = CBitcoinAddress(r['address']) + if 'pubkey' in r: + r['pubkey'] = unhexlify(r['pubkey']) return r + + def _addnode(self, node, arg): + r = self._call('addnode', node, arg) + return r + + def addnode(self, node): + return self._addnode(node, 'add') + + def addnodeonetry(self, node): + return self._addnode(node, 'onetry') + + def removenode(self, node): + return self._addnode(node, 'remove') + +__all__ = ( + 'JSONRPCException', + 'RawProxy', + 'Proxy', +) diff --git a/bitcoin/script.py b/bitcoin/script.py deleted file mode 100644 index da27059..0000000 --- a/bitcoin/script.py +++ /dev/null @@ -1,531 +0,0 @@ - -# -# script.py -# -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -from __future__ import absolute_import, division, print_function, unicode_literals - -import struct - -SIGHASH_ALL = 1 -SIGHASH_NONE = 2 -SIGHASH_SINGLE = 3 -SIGHASH_ANYONECANPAY = 0x80 - -# push value -OP_0 = 0x00 -OP_FALSE = OP_0 -OP_PUSHDATA1 = 0x4c -OP_PUSHDATA2 = 0x4d -OP_PUSHDATA4 = 0x4e -OP_1NEGATE = 0x4f -OP_RESERVED = 0x50 -OP_1 = 0x51 -OP_TRUE=OP_1 -OP_2 = 0x52 -OP_3 = 0x53 -OP_4 = 0x54 -OP_5 = 0x55 -OP_6 = 0x56 -OP_7 = 0x57 -OP_8 = 0x58 -OP_9 = 0x59 -OP_10 = 0x5a -OP_11 = 0x5b -OP_12 = 0x5c -OP_13 = 0x5d -OP_14 = 0x5e -OP_15 = 0x5f -OP_16 = 0x60 - -# control -OP_NOP = 0x61 -OP_VER = 0x62 -OP_IF = 0x63 -OP_NOTIF = 0x64 -OP_VERIF = 0x65 -OP_VERNOTIF = 0x66 -OP_ELSE = 0x67 -OP_ENDIF = 0x68 -OP_VERIFY = 0x69 -OP_RETURN = 0x6a - -# stack ops -OP_TOALTSTACK = 0x6b -OP_FROMALTSTACK = 0x6c -OP_2DROP = 0x6d -OP_2DUP = 0x6e -OP_3DUP = 0x6f -OP_2OVER = 0x70 -OP_2ROT = 0x71 -OP_2SWAP = 0x72 -OP_IFDUP = 0x73 -OP_DEPTH = 0x74 -OP_DROP = 0x75 -OP_DUP = 0x76 -OP_NIP = 0x77 -OP_OVER = 0x78 -OP_PICK = 0x79 -OP_ROLL = 0x7a -OP_ROT = 0x7b -OP_SWAP = 0x7c -OP_TUCK = 0x7d - -# splice ops -OP_CAT = 0x7e -OP_SUBSTR = 0x7f -OP_LEFT = 0x80 -OP_RIGHT = 0x81 -OP_SIZE = 0x82 - -# bit logic -OP_INVERT = 0x83 -OP_AND = 0x84 -OP_OR = 0x85 -OP_XOR = 0x86 -OP_EQUAL = 0x87 -OP_EQUALVERIFY = 0x88 -OP_RESERVED1 = 0x89 -OP_RESERVED2 = 0x8a - -# numeric -OP_1ADD = 0x8b -OP_1SUB = 0x8c -OP_2MUL = 0x8d -OP_2DIV = 0x8e -OP_NEGATE = 0x8f -OP_ABS = 0x90 -OP_NOT = 0x91 -OP_0NOTEQUAL = 0x92 - -OP_ADD = 0x93 -OP_SUB = 0x94 -OP_MUL = 0x95 -OP_DIV = 0x96 -OP_MOD = 0x97 -OP_LSHIFT = 0x98 -OP_RSHIFT = 0x99 - -OP_BOOLAND = 0x9a -OP_BOOLOR = 0x9b -OP_NUMEQUAL = 0x9c -OP_NUMEQUALVERIFY = 0x9d -OP_NUMNOTEQUAL = 0x9e -OP_LESSTHAN = 0x9f -OP_GREATERTHAN = 0xa0 -OP_LESSTHANOREQUAL = 0xa1 -OP_GREATERTHANOREQUAL = 0xa2 -OP_MIN = 0xa3 -OP_MAX = 0xa4 - -OP_WITHIN = 0xa5 - -# crypto -OP_RIPEMD160 = 0xa6 -OP_SHA1 = 0xa7 -OP_SHA256 = 0xa8 -OP_HASH160 = 0xa9 -OP_HASH256 = 0xaa -OP_CODESEPARATOR = 0xab -OP_CHECKSIG = 0xac -OP_CHECKSIGVERIFY = 0xad -OP_CHECKMULTISIG = 0xae -OP_CHECKMULTISIGVERIFY = 0xaf - -# expansion -OP_NOP1 = 0xb0 -OP_NOP2 = 0xb1 -OP_NOP3 = 0xb2 -OP_NOP4 = 0xb3 -OP_NOP5 = 0xb4 -OP_NOP6 = 0xb5 -OP_NOP7 = 0xb6 -OP_NOP8 = 0xb7 -OP_NOP9 = 0xb8 -OP_NOP10 = 0xb9 - -# template matching params -OP_SMALLINTEGER = 0xfa -OP_PUBKEYS = 0xfb -OP_PUBKEYHASH = 0xfd -OP_PUBKEY = 0xfe - -OP_INVALIDOPCODE = 0xff - -VALID_OPCODES = { - OP_1NEGATE, - OP_RESERVED, - OP_1, - OP_2, - OP_3, - OP_4, - OP_5, - OP_6, - OP_7, - OP_8, - OP_9, - OP_10, - OP_11, - OP_12, - OP_13, - OP_14, - OP_15, - OP_16, - - OP_NOP, - OP_VER, - OP_IF, - OP_NOTIF, - OP_VERIF, - OP_VERNOTIF, - OP_ELSE, - OP_ENDIF, - OP_VERIFY, - OP_RETURN, - - OP_TOALTSTACK, - OP_FROMALTSTACK, - OP_2DROP, - OP_2DUP, - OP_3DUP, - OP_2OVER, - OP_2ROT, - OP_2SWAP, - OP_IFDUP, - OP_DEPTH, - OP_DROP, - OP_DUP, - OP_NIP, - OP_OVER, - OP_PICK, - OP_ROLL, - OP_ROT, - OP_SWAP, - OP_TUCK, - - OP_CAT, - OP_SUBSTR, - OP_LEFT, - OP_RIGHT, - OP_SIZE, - - OP_INVERT, - OP_AND, - OP_OR, - OP_XOR, - OP_EQUAL, - OP_EQUALVERIFY, - OP_RESERVED1, - OP_RESERVED2, - - OP_1ADD, - OP_1SUB, - OP_2MUL, - OP_2DIV, - OP_NEGATE, - OP_ABS, - OP_NOT, - OP_0NOTEQUAL, - - OP_ADD, - OP_SUB, - OP_MUL, - OP_DIV, - OP_MOD, - OP_LSHIFT, - OP_RSHIFT, - - OP_BOOLAND, - OP_BOOLOR, - OP_NUMEQUAL, - OP_NUMEQUALVERIFY, - OP_NUMNOTEQUAL, - OP_LESSTHAN, - OP_GREATERTHAN, - OP_LESSTHANOREQUAL, - OP_GREATERTHANOREQUAL, - OP_MIN, - OP_MAX, - - OP_WITHIN, - - OP_RIPEMD160, - OP_SHA1, - OP_SHA256, - OP_HASH160, - OP_HASH256, - OP_CODESEPARATOR, - OP_CHECKSIG, - OP_CHECKSIGVERIFY, - OP_CHECKMULTISIG, - OP_CHECKMULTISIGVERIFY, - - OP_NOP1, - OP_NOP2, - OP_NOP3, - OP_NOP4, - OP_NOP5, - OP_NOP6, - OP_NOP7, - OP_NOP8, - OP_NOP9, - OP_NOP10, - - OP_SMALLINTEGER, - OP_PUBKEYS, - OP_PUBKEYHASH, - OP_PUBKEY, -} - -OPCODE_NAMES = { - OP_0 : 'OP_0', - OP_PUSHDATA1 : 'OP_PUSHDATA1', - OP_PUSHDATA2 : 'OP_PUSHDATA2', - OP_PUSHDATA4 : 'OP_PUSHDATA4', - OP_1NEGATE : 'OP_1NEGATE', - OP_RESERVED : 'OP_RESERVED', - OP_1 : 'OP_1', - OP_2 : 'OP_2', - OP_3 : 'OP_3', - OP_4 : 'OP_4', - OP_5 : 'OP_5', - OP_6 : 'OP_6', - OP_7 : 'OP_7', - OP_8 : 'OP_8', - OP_9 : 'OP_9', - OP_10 : 'OP_10', - OP_11 : 'OP_11', - OP_12 : 'OP_12', - OP_13 : 'OP_13', - OP_14 : 'OP_14', - OP_15 : 'OP_15', - OP_16 : 'OP_16', - OP_NOP : 'OP_NOP', - OP_VER : 'OP_VER', - OP_IF : 'OP_IF', - OP_NOTIF : 'OP_NOTIF', - OP_VERIF : 'OP_VERIF', - OP_VERNOTIF : 'OP_VERNOTIF', - OP_ELSE : 'OP_ELSE', - OP_ENDIF : 'OP_ENDIF', - OP_VERIFY : 'OP_VERIFY', - OP_RETURN : 'OP_RETURN', - OP_TOALTSTACK : 'OP_TOALTSTACK', - OP_FROMALTSTACK : 'OP_FROMALTSTACK', - OP_2DROP : 'OP_2DROP', - OP_2DUP : 'OP_2DUP', - OP_3DUP : 'OP_3DUP', - OP_2OVER : 'OP_2OVER', - OP_2ROT : 'OP_2ROT', - OP_2SWAP : 'OP_2SWAP', - OP_IFDUP : 'OP_IFDUP', - OP_DEPTH : 'OP_DEPTH', - OP_DROP : 'OP_DROP', - OP_DUP : 'OP_DUP', - OP_NIP : 'OP_NIP', - OP_OVER : 'OP_OVER', - OP_PICK : 'OP_PICK', - OP_ROLL : 'OP_ROLL', - OP_ROT : 'OP_ROT', - OP_SWAP : 'OP_SWAP', - OP_TUCK : 'OP_TUCK', - OP_CAT : 'OP_CAT', - OP_SUBSTR : 'OP_SUBSTR', - OP_LEFT : 'OP_LEFT', - OP_RIGHT : 'OP_RIGHT', - OP_SIZE : 'OP_SIZE', - OP_INVERT : 'OP_INVERT', - OP_AND : 'OP_AND', - OP_OR : 'OP_OR', - OP_XOR : 'OP_XOR', - OP_EQUAL : 'OP_EQUAL', - OP_EQUALVERIFY : 'OP_EQUALVERIFY', - OP_RESERVED1 : 'OP_RESERVED1', - OP_RESERVED2 : 'OP_RESERVED2', - OP_1ADD : 'OP_1ADD', - OP_1SUB : 'OP_1SUB', - OP_2MUL : 'OP_2MUL', - OP_2DIV : 'OP_2DIV', - OP_NEGATE : 'OP_NEGATE', - OP_ABS : 'OP_ABS', - OP_NOT : 'OP_NOT', - OP_0NOTEQUAL : 'OP_0NOTEQUAL', - OP_ADD : 'OP_ADD', - OP_SUB : 'OP_SUB', - OP_MUL : 'OP_MUL', - OP_DIV : 'OP_DIV', - OP_MOD : 'OP_MOD', - OP_LSHIFT : 'OP_LSHIFT', - OP_RSHIFT : 'OP_RSHIFT', - OP_BOOLAND : 'OP_BOOLAND', - OP_BOOLOR : 'OP_BOOLOR', - OP_NUMEQUAL : 'OP_NUMEQUAL', - OP_NUMEQUALVERIFY : 'OP_NUMEQUALVERIFY', - OP_NUMNOTEQUAL : 'OP_NUMNOTEQUAL', - OP_LESSTHAN : 'OP_LESSTHAN', - OP_GREATERTHAN : 'OP_GREATERTHAN', - OP_LESSTHANOREQUAL : 'OP_LESSTHANOREQUAL', - OP_GREATERTHANOREQUAL : 'OP_GREATERTHANOREQUAL', - OP_MIN : 'OP_MIN', - OP_MAX : 'OP_MAX', - OP_WITHIN : 'OP_WITHIN', - OP_RIPEMD160 : 'OP_RIPEMD160', - OP_SHA1 : 'OP_SHA1', - OP_SHA256 : 'OP_SHA256', - OP_HASH160 : 'OP_HASH160', - OP_HASH256 : 'OP_HASH256', - OP_CODESEPARATOR : 'OP_CODESEPARATOR', - OP_CHECKSIG : 'OP_CHECKSIG', - OP_CHECKSIGVERIFY : 'OP_CHECKSIGVERIFY', - OP_CHECKMULTISIG : 'OP_CHECKMULTISIG', - OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY', - OP_NOP1 : 'OP_NOP1', - OP_NOP2 : 'OP_NOP2', - OP_NOP3 : 'OP_NOP3', - OP_NOP4 : 'OP_NOP4', - OP_NOP5 : 'OP_NOP5', - OP_NOP6 : 'OP_NOP6', - OP_NOP7 : 'OP_NOP7', - OP_NOP8 : 'OP_NOP8', - OP_NOP9 : 'OP_NOP9', - OP_NOP10 : 'OP_NOP10', - OP_SMALLINTEGER : 'OP_SMALLINTEGER', - OP_PUBKEYS : 'OP_PUBKEYS', - OP_PUBKEYHASH : 'OP_PUBKEYHASH', - OP_PUBKEY : 'OP_PUBKEY', -} - -TEMPLATES = [ - [ OP_PUBKEY, OP_CHECKSIG ], - [ OP_DUP, OP_HASH160, OP_PUBKEYHASH, OP_EQUALVERIFY, OP_CHECKSIG ], -] - -class CScriptOp(object): - def __init__(self): - self.op = OP_INVALIDOPCODE - self.data = '' - self.ser_len = 0 - -class CScript(object): - def __init__(self, vch=None): - self.vch = vch - - self.reset() - - def reset(self): - self.pc = 0 - if self.vch is None: - self.pend = 0 - else: - self.pend = len(self.vch) - self.pbegincodehash = 0 - self.sop = None - - def getchars(self, n): - if (self.pc + n) > self.pend: - return None - - s = self.vch[self.pc:self.pc+n] - self.pc += n - - return s - - def getop(self): - s = self.getchars(1) - if s is None: - return False - opcode = ord(s) - - sop = CScriptOp() - sop.op = opcode - sop.ser_len = 1 - - if opcode > OP_PUSHDATA4: - if opcode not in VALID_OPCODES: - return False - self.sop = sop - return True - - if opcode < OP_PUSHDATA1: - datasize = opcode - - elif opcode == OP_PUSHDATA1: - sop.ser_len += 1 - s = self.getchars(1) - if s is None: - return False - datasize = ord(s) - - elif opcode == OP_PUSHDATA2: - sop.ser_len += 2 - s = self.getchars(2) - if s is None: - return False - datasize = struct.unpack(b"= len(template): - return None - if not self.getop(): - return None - - expected_op = template[i] - if expected_op == OP_PUBKEYHASH or expected_op == OP_PUBKEY: - if self.sop.op > OP_PUSHDATA4: - return None - l.append(self.sop.data) - - elif self.sop.op != expected_op: - return None - - i += 1 - - return l - - def match_alltemp(self, vch_in=None): - for temp in TEMPLATES: - l = self.match_temp(temp, vch_in) - if l is not None: - return l - return None - - def __repr__(self): - return "CScript(vchsz %d)" % (len(self.vch),) - diff --git a/bitcoin/scripteval.py b/bitcoin/scripteval.py deleted file mode 100644 index f104920..0000000 --- a/bitcoin/scripteval.py +++ /dev/null @@ -1,619 +0,0 @@ - -# -# scripteval.py -# -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -from __future__ import absolute_import, division, print_function, unicode_literals - -import sys -if sys.version > '3': - long = int - -import hashlib -from bitcoin.serialize import Hash, Hash160, ser_uint256, ser_uint160 -from bitcoin.script import * -from bitcoin.core import CTxOut, CTransaction -from bitcoin.key import CKey -from bitcoin.bignum import bn2vch, vch2bn - -def SignatureHash(script, txTo, inIdx, hashtype): - if inIdx >= len(txTo.vin): - return (1, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin))) - txtmp = CTransaction() - txtmp.copy(txTo) - - for txin in txtmp.vin: - txin.scriptSig = b'' - txtmp.vin[inIdx].scriptSig = script.vch - - if (hashtype & 0x1f) == SIGHASH_NONE: - txtmp.vout = [] - - for i in range(len(txtmp.vin)): - if i != inIdx: - txtmp.vin[i].nSequence = 0 - - elif (hashtype & 0x1f) == SIGHASH_SINGLE: - outIdx = inIdx - if outIdx >= len(txtmp.vout): - return (1, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout))) - - tmp = txtmp.vout[outIdx] - txtmp.vout = [] - for i in range(outIdx): - txtmp.vout.append(CTxOut()) - txtmp.vout.append(tmp) - - for i in range(len(txtmp.vin)): - if i != inIdx: - txtmp.vin[i].nSequence = 0 - - if hashtype & SIGHASH_ANYONECANPAY: - tmp = txtmp.vin[inIdx] - txtmp.vin = [] - txtmp.vin.append(tmp) - - s = txtmp.serialize() - s += struct.pack(b" 20: - return False - i += 1 - ikey = i - i += keys_count - if len(stack) < i: - return False - - sigs_count = CastToBigNum(stack[-i]) - if sigs_count < 0 or sigs_count > keys_count: - return False - i += 1 - isig = i - i += sigs_count - if len(stack) < i: - return False - - for k in range(sigs_count): - sig = stack[-isig-k] - # FIXME: find-and-delete sig in script - - success = True - - while success and sigs_count > 0: - sig = stack[-isig] - pubkey = stack[-ikey] - - if CheckSig(sig, pubkey, script, txTo, inIdx, hashtype): - isig += 1 - sigs_count -= 1 - - ikey += 1 - keys_count -= 1 - - if sigs_count > keys_count: - success = False - - while i > 0: - stack.pop() - i -= 1 - - if success: - stack.append(b"\x01") - else: - stack.append(b"\x00") - - if opcode == OP_CHECKMULTISIGVERIFY: - if success: - stack.pop() - else: - return False - - return True - -def dumpstack(msg, stack): - print("%s stacksz %d" % (msg, len(stack))) - for i in range(len(stack)): - vch = stack[i] - print("#%d: %s" % (i, vch.encode('hex'))) - -ISA_UNOP = { - OP_1ADD, - OP_1SUB, - OP_2MUL, - OP_2DIV, - OP_NEGATE, - OP_ABS, - OP_NOT, - OP_0NOTEQUAL, -} - -def UnaryOp(opcode, stack): - if len(stack) < 1: - return False - bn = CastToBigNum(stack.pop()) - - if opcode == OP_1ADD: - bn += 1 - - elif opcode == OP_1SUB: - bn -= 1 - - elif opcode == OP_2MUL: - bn <<= 1 - - elif opcode == OP_2DIV: - bn >>= 1 - - elif opcode == OP_NEGATE: - bn = -bn - - elif opcode == OP_ABS: - if bn < 0: - bn = -bn - - elif opcode == OP_NOT: - bn = long(bn == 0) - - elif opcode == OP_0NOTEQUAL: - bn = long(bn != 0) - - else: - return False - - stack.append(bn2vch(bn)) - - return True - -ISA_BINOP = { - OP_ADD, - OP_SUB, - OP_LSHIFT, - OP_RSHIFT, - OP_BOOLAND, - OP_BOOLOR, - OP_NUMEQUAL, - OP_NUMEQUALVERIFY, - OP_NUMNOTEQUAL, - OP_LESSTHAN, - OP_GREATERTHAN, - OP_LESSTHANOREQUAL, - OP_GREATERTHANOREQUAL, - OP_MIN, - OP_MAX, -} - -def BinOp(opcode, stack): - if len(stack) < 2: - return False - - bn2 = CastToBigNum(stack.pop()) - bn1 = CastToBigNum(stack.pop()) - - if opcode == OP_ADD: - bn = bn1 + bn2 - - elif opcode == OP_SUB: - bn = bn1 - bn2 - - elif opcode == OP_LSHIFT: - if bn2 < 0 or bn2 > 2048: - return False - bn = bn1 << bn2 - - elif opcode == OP_RSHIFT: - if bn2 < 0 or bn2 > 2048: - return False - bn = bn1 >> bn2 - - elif opcode == OP_BOOLAND: - bn = long(bn1 != 0 and bn2 != 0) - - elif opcode == OP_BOOLOR: - bn = long(bn1 != 0 or bn2 != 0) - - elif opcode == OP_NUMEQUAL or opcode == OP_NUMEQUALVERIFY: - bn = long(bn1 == bn2) - - elif opcode == OP_NUMNOTEQUAL: - bn = long(bn1 != bn2) - - elif opcode == OP_LESSTHAN: - bn = long(bn1 < bn2) - - elif opcode == OP_GREATERTHAN: - bn = long(bn1 > bn2) - - elif opcode == OP_LESSTHANOREQUAL: - bn = long(bn1 <= bn2) - - elif opcode == OP_GREATERTHANOREQUAL: - bn = long(bn1 >= bn2) - - elif opcode == OP_MIN: - if bn1 < bn2: - bn = bn1 - else: - bn = bn2 - - elif opcode == OP_MAX: - if bn1 > bn2: - bn = bn1 - else: - bn = bn2 - - else: - return False # unknown binop opcode - - stack.append(bn2vch(bn)) - - if opcode == OP_NUMEQUALVERIFY: - if CastToBool(stack[-1]): - stack.pop() - else: - return False - - return True - -def CheckExec(vfExec): - for b in vfExec: - if not b: - return False - return True - -def EvalScript(stack, scriptIn, txTo, inIdx, hashtype): - altstack = [] - vfExec = [] - script = CScript(scriptIn) - while script.pc < script.pend: - if not script.getop(): - return False - sop = script.sop - - fExec = CheckExec(vfExec) - - if fExec and sop.op <= OP_PUSHDATA4: - stack.append(sop.data) - continue - - elif fExec and sop.op == OP_1NEGATE or ((sop.op >= OP_1) and (sop.op <= OP_16)): - v = sop.op - (OP_1 - 1) - stack.append(bn2vch(v)) - - elif fExec and sop.op in ISA_BINOP: - if not BinOp(sop.op, stack): - return False - - elif fExec and sop.op in ISA_UNOP: - if not UnaryOp(sop.op, stack): - return False - - elif fExec and sop.op == OP_2DROP: - if len(stack) < 2: - return False - stack.pop() - stack.pop() - - elif fExec and sop.op == OP_2DUP: - if len(stack) < 2: - return False - v1 = stack[-2] - v2 = stack[-1] - stack.append(v1) - stack.append(v2) - - elif fExec and sop.op == OP_2OVER: - if len(stack) < 4: - return False - v1 = stack[-4] - v2 = stack[-3] - stack.append(v1) - stack.append(v2) - - elif fExec and sop.op == OP_2SWAP: - if len(stack) < 4: - return False - tmp = stack[-4] - stack[-4] = stack[-2] - stack[-2] = tmp - - tmp = stack[-3] - stack[-3] = stack[-1] - stack[-1] = tmp - - elif fExec and sop.op == OP_3DUP: - if len(stack) < 3: - return False - v1 = stack[-3] - v2 = stack[-2] - v3 = stack[-1] - stack.append(v1) - stack.append(v2) - stack.append(v3) - - elif fExec and sop.op == OP_CHECKMULTISIG or sop.op == OP_CHECKMULTISIGVERIFY: - tmpScript = CScript(script.vch[script.pbegincodehash:script.pend]) - ok = CheckMultiSig(sop.op, tmpScript, stack, txTo, - inIdx, hashtype) - if not ok: - return False - - elif fExec and sop.op == OP_CHECKSIG or sop.op == OP_CHECKSIGVERIFY: - if len(stack) < 2: - return False - vchPubKey = stack.pop() - vchSig = stack.pop() - tmpScript = CScript(script.vch[script.pbegincodehash:script.pend]) - - # FIXME: find-and-delete vchSig - - ok = CheckSig(vchSig, vchPubKey, tmpScript, - txTo, inIdx, hashtype) - if ok: - if sop.op != OP_CHECKSIGVERIFY: - stack.append(b"\x01") - else: - if sop.op == OP_CHECKSIGVERIFY: - return False - stack.append(b"\x00") - - elif fExec and sop.op == OP_CODESEPARATOR: - script.pbegincodehash = script.pc - - elif fExec and sop.op == OP_DEPTH: - bn = len(stack) - stack.append(bn2vch(bn)) - - elif fExec and sop.op == OP_DROP: - if len(stack) < 1: - return False - stack.pop() - - elif fExec and sop.op == OP_DUP: - if len(stack) < 1: - return False - v = stack[-1] - stack.append(v) - - elif sop.op == OP_ELSE: - if len(vfExec) == 0: - return false - vfExec[-1] = not vfExec[-1] - - elif sop.op == OP_ENDIF: - if len(vfExec) == 0: - return false - vfExec.pop() - - elif fExec and sop.op == OP_EQUAL or sop.op == OP_EQUALVERIFY: - if len(stack) < 2: - return False - v1 = stack.pop() - v2 = stack.pop() - - is_equal = (v1 == v2) - if is_equal: - stack.append(b"\x01") - else: - stack.append(b"\x00") - - if sop.op == OP_EQUALVERIFY: - if is_equal: - stack.pop() - else: - return False - - elif fExec and sop.op == OP_FROMALTSTACK: - if len(altstack) < 1: - return False - v = altstack.pop() - stack.append(v) - - elif fExec and sop.op == OP_HASH160: - if len(stack) < 1: - return False - stack.append(ser_uint160(Hash160(stack.pop()))) - - elif fExec and sop.op == OP_HASH256: - if len(stack) < 1: - return False - stack.append(ser_uint256(Hash(stack.pop()))) - - elif sop.op == OP_IF or sop.op == OP_NOTIF: - val = False - - if fExec: - if len(stack) < 1: - return False - vch = stack.pop() - val = CastToBool(vch) - if sop.op == OP_NOTIF: - val = not val - - vfExec.append(val) - - elif fExec and sop.op == OP_IFDUP: - if len(stack) < 1: - return False - vch = stack[-1] - if CastToBool(vch): - stack.append(vch) - - elif fExec and sop.op == OP_NIP: - if len(stack) < 2: - return False - del stack[-2] - - elif fExec and sop.op == OP_NOP or (sop.op >= OP_NOP1 and sop.op <= OP_NOP10): - pass - - elif fExec and sop.op == OP_OVER: - if len(stack) < 2: - return False - vch = stack[-2] - stack.append(vch) - - elif fExec and sop.op == OP_PICK or sop.op == OP_ROLL: - if len(stack) < 2: - return False - n = CastToBigNum(stack.pop()) - if n < 0 or n >= len(stack): - return False - vch = stack[-n-1] - if sop.op == OP_ROLL: - del stack[-n-1] - stack.append(vch) - - elif fExec and sop.op == OP_RETURN: - return False - - elif fExec and sop.op == OP_RIPEMD160: - if len(stack) < 1: - return False - - h = hashlib.new('ripemd160') - h.update(stack.pop()) - stack.append(h.digest()) - - elif fExec and sop.op == OP_ROT: - if len(stack) < 3: - return False - tmp = stack[-3] - stack[-3] = stack[-2] - stack[-2] = tmp - - tmp = stack[-2] - stack[-2] = stack[-1] - stack[-1] = tmp - - elif fExec and sop.op == OP_SIZE: - if len(stack) < 1: - return False - bn = len(stack[-1]) - stack.append(bn2vch(bn)) - - elif fExec and sop.op == OP_SHA256: - if len(stack) < 1: - return False - stack.append(hashlib.sha256(stack.pop()).digest()) - - elif fExec and sop.op == OP_SWAP: - if len(stack) < 2: - return False - tmp = stack[-2] - stack[-2] = stack[-1] - stack[-1] = tmp - - elif fExec and sop.op == OP_TOALTSTACK: - if len(stack) < 1: - return False - v = stack.pop() - altstack.append(v) - - elif fExec and sop.op == OP_TUCK: - if len(stack) < 2: - return False - vch = stack[-1] - stack.insert(len(stack) - 2, vch) - - elif fExec and sop.op == OP_VERIFY: - if len(stack) < 1: - return False - v = CastToBool(stack[-1]) - if v: - stack.pop() - else: - return False - - elif fExec and sop.op == OP_WITHIN: - if len(stack) < 3: - return False - bn3 = CastToBigNum(stack.pop()) - bn2 = CastToBigNum(stack.pop()) - bn1 = CastToBigNum(stack.pop()) - v = (bn2 <= bn1) and (bn1 < bn3) - if v: - stack.append(b"\x01") - else: - stack.append(b"\x00") - - elif fExec: - #print("Unsupported opcode", OPCODE_NAMES[sop.op]) - return False - - return True - -def CastToBigNum(s): - v = vch2bn(s) - return v - -def CastToBool(s): - for i in range(len(s)): - sv = ord(s[i]) - if sv != 0: - if (i == (len(s) - 1)) and (sv == 0x80): - return False - return True - - return False - -def VerifyScript(scriptSig, scriptPubKey, txTo, inIdx, hashtype): - stack = [] - if not EvalScript(stack, scriptSig, txTo, inIdx, hashtype): - return False - if not EvalScript(stack, scriptPubKey, txTo, inIdx, hashtype): - return False - if len(stack) == 0: - return False - return CastToBool(stack[-1]) - -def VerifySignature(txFrom, txTo, inIdx, hashtype): - if inIdx >= len(txTo.vin): - return False - txin = txTo.vin[inIdx] - - if txin.prevout.n >= len(txFrom.vout): - return False - txout = txFrom.vout[txin.prevout.n] - - txFrom.calc_sha256() - - if txin.prevout.hash != txFrom.sha256: - return False - - if not VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, inIdx, - hashtype): - return False - - return True - - - - diff --git a/bitcoin/serialize.py b/bitcoin/serialize.py deleted file mode 100644 index 7734dac..0000000 --- a/bitcoin/serialize.py +++ /dev/null @@ -1,206 +0,0 @@ - -# -# serialize.py -# -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -from __future__ import absolute_import, division, print_function, unicode_literals - -import struct -import hashlib - -# Py3 compatibility -import sys -bchr = chr -if sys.version > '3': - bchr = lambda x: bytes([x]) - -def deser_string(f): - nit = struct.unpack(b">= 32 - return rs - -def ser_uint160(u): - rs = b"" - for i in range(5): - rs += struct.pack(b">= 32 - return rs - -def uint160_from_str(s): - r = 0 - t = struct.unpack(b"> 24) & 0xFF - v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) - return v - -def uint256_to_shortstr(u): - s = "%064x" % (u,) - return s[:16] - -def deser_vector(f, c, arg1=None): - nit = struct.unpack(b"520 byte push"], +["0", +"IF 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' ENDIF 1", +">520 byte push in non-executed IF branch"], +["1", +"0x61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +">201 opcodes executed. 0x61 is NOP"], +["0", +"IF 0x6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 ENDIF 1", +">201 opcodes including non-executed IF branch. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +">1,000 stack size (0x6f is 3DUP)"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 TOALTSTACK 2 TOALTSTACK 3 4 5 6 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +">1,000 stack+altstack size"], +["NOP", +"0 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"10,001-byte scriptPubKey"], + +["NOP1","NOP10"], + +["1","VER", "OP_VER is reserved"], +["1","VERIF", "OP_VERIF is reserved"], +["1","VERNOTIF", "OP_VERNOTIF is reserved"], +["1","RESERVED", "OP_RESERVED is reserved"], +["1","RESERVED1", "OP_RESERVED1 is reserved"], +["1","RESERVED2", "OP_RESERVED2 is reserved"], +["1","0xba", "0xba == OP_NOP10 + 1"], + +["2147483648", "1ADD 1", "We cannot do math on 5-byte integers"], +["-2147483648", "1ADD 1", "Because we use a sign bit, -2147483648 is also 5 bytes"], + +["1", "1 ENDIF", "ENDIF without IF"], +["1", "IF 1", "IF without ENDIF"], +["1 IF 1", "ENDIF", "IFs don't carry over"], + +["NOP", "IF 1 ENDIF", "The following tests check the if(stack.size() < N) tests in each opcode"], +["NOP", "NOTIF 1 ENDIF", "They are here to catch copy-and-paste errors"], +["NOP", "VERIFY 1", "Most of them are duplicated elsewhere,"], + +["NOP", "TOALTSTACK 1", "but, hey, more is always better, right?"], +["1", "FROMALTSTACK"], +["1", "2DROP 1"], +["1", "2DUP"], +["1 1", "3DUP"], +["1 1 1", "2OVER"], +["1 1 1 1 1", "2ROT"], +["1 1 1", "2SWAP"], +["NOP", "IFDUP 1"], +["NOP", "DROP 1"], +["NOP", "DUP 1"], +["1", "NIP"], +["1", "OVER"], +["1 1 1 3", "PICK"], +["0", "PICK 1"], +["1 1 1 3", "ROLL"], +["0", "ROLL 1"], +["1 1", "ROT"], +["1", "SWAP"], +["1", "TUCK"], + +["NOP", "SIZE 1"], + +["1", "EQUAL 1"], +["1", "EQUALVERIFY 1"], + +["NOP", "1ADD 1"], +["NOP", "1SUB 1"], +["NOP", "NEGATE 1"], +["NOP", "ABS 1"], +["NOP", "NOT 1"], +["NOP", "0NOTEQUAL 1"], + +["1", "ADD"], +["1", "SUB"], +["1", "BOOLAND"], +["1", "BOOLOR"], +["1", "NUMEQUAL"], +["1", "NUMEQUALVERIFY 1"], +["1", "NUMNOTEQUAL"], +["1", "LESSTHAN"], +["1", "GREATERTHAN"], +["1", "LESSTHANOREQUAL"], +["1", "GREATERTHANOREQUAL"], +["1", "MIN"], +["1", "MAX"], +["1 1", "WITHIN"], + +["NOP", "RIPEMD160 1"], +["NOP", "SHA1 1"], +["NOP", "SHA256 1"], +["NOP", "HASH160 1"], +["NOP", "HASH256 1"], + +["", +"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG", +"202 CHECKMULTISIGS, fails due to 201 op limit"], + +["1", +"0 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY 0 0 CHECKMULTISIGVERIFY"], + +["", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", +"Fails due to 201 sig op limit"], + +["1", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY"], + + + +["NOP 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Tests for Script.IsPushOnly()"], +["NOP1 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], + +["0 0x01 0x50", "HASH160 0x14 0xece424a6bb6ddf4db592c0faed60685047a361b1 EQUAL", "OP_RESERVED in P2SH should fail"], +["0 0x01 VER", "HASH160 0x14 0x0f4d7845db968f2a81b530b6f3c1d6246d4c7e01 EQUAL", "OP_VER in P2SH should fail"], + +["0x00", "'00' EQUAL", "Basic OP_0 execution"] +] diff --git a/bitcoin/tests/data/script_valid.json b/bitcoin/tests/data/script_valid.json new file mode 100644 index 0000000..162e5c3 --- /dev/null +++ b/bitcoin/tests/data/script_valid.json @@ -0,0 +1,493 @@ +[ +["", "DEPTH 0 EQUAL", "Test the test: we should have an empty stack after scriptSig evaluation"], +[" ", "DEPTH 0 EQUAL", "and multiple spaces should not change that."], +[" ", "DEPTH 0 EQUAL"], +[" ", "DEPTH 0 EQUAL"], +["1 2", "2 EQUALVERIFY 1 EQUAL", "Similarly whitespace around and between symbols"], +["1 2", "2 EQUALVERIFY 1 EQUAL"], +[" 1 2", "2 EQUALVERIFY 1 EQUAL"], +["1 2 ", "2 EQUALVERIFY 1 EQUAL"], +[" 1 2 ", "2 EQUALVERIFY 1 EQUAL"], + +["1", ""], + +["0x01 0x0b", "11 EQUAL", "push 1 byte"], +["0x02 0x417a", "'Az' EQUAL"], +["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a", + "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "push 75 bytes"], + +["0x4c 0x01 0x07","7 EQUAL", "0x4c is OP_PUSHDATA1"], +["0x4d 0x0100 0x08","8 EQUAL", "0x4d is OP_PUSHDATA2"], +["0x4e 0x01000000 0x09","9 EQUAL", "0x4e is OP_PUSHDATA4"], + +["0x4c 0x00","0 EQUAL"], +["0x4d 0x0000","0 EQUAL"], +["0x4e 0x00000000","0 EQUAL"], +["0x4f 1000 ADD","999 EQUAL"], +["0", "IF 0x50 ENDIF 1", "0x50 is reserved (ok if not executed)"], +["0x51", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"], +["1","NOP"], +["0", "IF VER ELSE 1 ENDIF", "VER non-functional (ok if not executed)"], +["0", "IF RESERVED RESERVED1 RESERVED2 ELSE 1 ENDIF", "RESERVED ok in un-executed IF"], + +["1", "DUP IF ENDIF"], +["1", "IF 1 ENDIF"], +["1", "DUP IF ELSE ENDIF"], +["1", "IF 1 ELSE ENDIF"], +["0", "IF ELSE 1 ENDIF"], + +["1 1", "IF IF 1 ELSE 0 ENDIF ENDIF"], +["1 0", "IF IF 1 ELSE 0 ENDIF ENDIF"], +["1 1", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], +["0 0", "IF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], + +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"], +["1 1", "NOTIF IF 1 ELSE 0 ENDIF ENDIF"], +["1 0", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], +["0 1", "NOTIF IF 1 ELSE 0 ENDIF ELSE IF 0 ELSE 1 ENDIF ENDIF"], + +["0", "IF 0 ELSE 1 ELSE 0 ENDIF", "Multiple ELSE's are valid and executed inverts on each ELSE encountered"], +["1", "IF 1 ELSE 0 ELSE ENDIF"], +["1", "IF ELSE 0 ELSE 1 ENDIF"], +["1", "IF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL"], +["'' 1", "IF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL"], + +["1", "NOTIF 0 ELSE 1 ELSE 0 ENDIF", "Multiple ELSE's are valid and execution inverts on each ELSE encountered"], +["0", "NOTIF 1 ELSE 0 ELSE ENDIF"], +["0", "NOTIF ELSE 0 ELSE 1 ENDIF"], +["0", "NOTIF 1 ELSE 0 ELSE 1 ENDIF ADD 2 EQUAL"], +["'' 0", "NOTIF SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ELSE ELSE SHA1 ENDIF 0x14 0x68ca4fec736264c13b859bac43d5173df6871682 EQUAL"], + +["0", "IF 1 IF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 1 IF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL", "Nested ELSE ELSE"], +["1", "NOTIF 0 NOTIF RETURN ELSE RETURN ELSE RETURN ENDIF ELSE 0 NOTIF 1 ELSE RETURN ELSE 1 ENDIF ELSE RETURN ENDIF ADD 2 EQUAL"], + +["0", "IF RETURN ENDIF 1", "RETURN only works if executed"], + +["1 1", "VERIFY"], + +["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL"], +["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL"], + +["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL"], +["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL"], +["0 DROP", "DEPTH 0 EQUAL"], +["0", "DUP 1 ADD 1 EQUALVERIFY 0 EQUAL"], +["0 1", "NIP"], +["1 0", "OVER DEPTH 3 EQUALVERIFY"], +["22 21 20", "0 PICK 20 EQUALVERIFY DEPTH 3 EQUAL"], +["22 21 20", "1 PICK 21 EQUALVERIFY DEPTH 3 EQUAL"], +["22 21 20", "2 PICK 22 EQUALVERIFY DEPTH 3 EQUAL"], +["22 21 20", "0 ROLL 20 EQUALVERIFY DEPTH 2 EQUAL"], +["22 21 20", "1 ROLL 21 EQUALVERIFY DEPTH 2 EQUAL"], +["22 21 20", "2 ROLL 22 EQUALVERIFY DEPTH 2 EQUAL"], +["22 21 20", "ROT 22 EQUAL"], +["22 21 20", "ROT DROP 20 EQUAL"], +["22 21 20", "ROT DROP DROP 21 EQUAL"], +["22 21 20", "ROT ROT 21 EQUAL"], +["22 21 20", "ROT ROT ROT 20 EQUAL"], +["25 24 23 22 21 20", "2ROT 24 EQUAL"], +["25 24 23 22 21 20", "2ROT DROP 25 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP 20 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP DROP 21 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP 22 EQUAL"], +["25 24 23 22 21 20", "2ROT 2DROP 2DROP DROP 23 EQUAL"], +["25 24 23 22 21 20", "2ROT 2ROT 22 EQUAL"], +["25 24 23 22 21 20", "2ROT 2ROT 2ROT 20 EQUAL"], +["1 0", "SWAP 1 EQUALVERIFY 0 EQUAL"], +["0 1", "TUCK DEPTH 3 EQUALVERIFY SWAP 2DROP"], +["13 14", "2DUP ROT EQUALVERIFY EQUAL"], +["-1 0 1 2", "3DUP DEPTH 7 EQUALVERIFY ADD ADD 3 EQUALVERIFY 2DROP 0 EQUALVERIFY"], +["1 2 3 5", "2OVER ADD ADD 8 EQUALVERIFY ADD ADD 6 EQUAL"], +["1 3 5 7", "2SWAP ADD 4 EQUALVERIFY ADD 12 EQUAL"], +["0", "SIZE 0 EQUAL"], +["1", "SIZE 1 EQUAL"], +["127", "SIZE 1 EQUAL"], +["128", "SIZE 2 EQUAL"], +["32767", "SIZE 2 EQUAL"], +["32768", "SIZE 3 EQUAL"], +["8388607", "SIZE 3 EQUAL"], +["8388608", "SIZE 4 EQUAL"], +["2147483647", "SIZE 4 EQUAL"], +["2147483648", "SIZE 5 EQUAL"], +["-1", "SIZE 1 EQUAL"], +["-127", "SIZE 1 EQUAL"], +["-128", "SIZE 2 EQUAL"], +["-32767", "SIZE 2 EQUAL"], +["-32768", "SIZE 3 EQUAL"], +["-8388607", "SIZE 3 EQUAL"], +["-8388608", "SIZE 4 EQUAL"], +["-2147483647", "SIZE 4 EQUAL"], +["-2147483648", "SIZE 5 EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "SIZE 26 EQUAL"], + + +["2 -2 ADD", "0 EQUAL"], +["2147483647 -2147483647 ADD", "0 EQUAL"], +["-1 -1 ADD", "-2 EQUAL"], + +["0 0","EQUAL"], +["1 1 ADD", "2 EQUAL"], +["1 1ADD", "2 EQUAL"], +["111 1SUB", "110 EQUAL"], +["111 1 ADD 12 SUB", "100 EQUAL"], +["0 ABS", "0 EQUAL"], +["16 ABS", "16 EQUAL"], +["-16 ABS", "-16 NEGATE EQUAL"], +["0 NOT", "NOP"], +["1 NOT", "0 EQUAL"], +["11 NOT", "0 EQUAL"], +["0 0NOTEQUAL", "0 EQUAL"], +["1 0NOTEQUAL", "1 EQUAL"], +["111 0NOTEQUAL", "1 EQUAL"], +["-111 0NOTEQUAL", "1 EQUAL"], +["1 1 BOOLAND", "NOP"], +["1 0 BOOLAND", "NOT"], +["0 1 BOOLAND", "NOT"], +["0 0 BOOLAND", "NOT"], +["16 17 BOOLAND", "NOP"], +["1 1 BOOLOR", "NOP"], +["1 0 BOOLOR", "NOP"], +["0 1 BOOLOR", "NOP"], +["0 0 BOOLOR", "NOT"], +["16 17 BOOLOR", "NOP"], +["11 10 1 ADD", "NUMEQUAL"], +["11 10 1 ADD", "NUMEQUALVERIFY 1"], +["11 10 1 ADD", "NUMNOTEQUAL NOT"], +["111 10 1 ADD", "NUMNOTEQUAL"], +["11 10", "LESSTHAN NOT"], +["4 4", "LESSTHAN NOT"], +["10 11", "LESSTHAN"], +["-11 11", "LESSTHAN"], +["-11 -10", "LESSTHAN"], +["11 10", "GREATERTHAN"], +["4 4", "GREATERTHAN NOT"], +["10 11", "GREATERTHAN NOT"], +["-11 11", "GREATERTHAN NOT"], +["-11 -10", "GREATERTHAN NOT"], +["11 10", "LESSTHANOREQUAL NOT"], +["4 4", "LESSTHANOREQUAL"], +["10 11", "LESSTHANOREQUAL"], +["-11 11", "LESSTHANOREQUAL"], +["-11 -10", "LESSTHANOREQUAL"], +["11 10", "GREATERTHANOREQUAL"], +["4 4", "GREATERTHANOREQUAL"], +["10 11", "GREATERTHANOREQUAL NOT"], +["-11 11", "GREATERTHANOREQUAL NOT"], +["-11 -10", "GREATERTHANOREQUAL NOT"], +["1 0 MIN", "0 NUMEQUAL"], +["0 1 MIN", "0 NUMEQUAL"], +["-1 0 MIN", "-1 NUMEQUAL"], +["0 -2147483647 MIN", "-2147483647 NUMEQUAL"], +["2147483647 0 MAX", "2147483647 NUMEQUAL"], +["0 100 MAX", "100 NUMEQUAL"], +["-100 0 MAX", "0 NUMEQUAL"], +["0 -2147483647 MAX", "0 NUMEQUAL"], +["0 0 1", "WITHIN"], +["1 0 1", "WITHIN NOT"], +["0 -2147483647 2147483647", "WITHIN"], +["-1 -100 100", "WITHIN"], +["11 -100 100", "WITHIN"], +["-2147483647 -100 100", "WITHIN NOT"], +["2147483647 -100 100", "WITHIN NOT"], + +["2147483647 2147483647 SUB", "0 EQUAL"], +["2147483647 DUP ADD", "4294967294 EQUAL", ">32 bit EQUAL is valid"], +["2147483647 NEGATE DUP ADD", "-4294967294 EQUAL"], + +["''", "RIPEMD160 0x14 0x9c1185a5c5e9fc54612808977ee8f548b2258d31 EQUAL"], +["'a'", "RIPEMD160 0x14 0x0bdc9d2d256b3ee9daae347be6f4dc835a467ffe EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "RIPEMD160 0x14 0xf71c27109c692c1b56bbdceb5b9d2865b3708dbc EQUAL"], +["''", "SHA1 0x14 0xda39a3ee5e6b4b0d3255bfef95601890afd80709 EQUAL"], +["'a'", "SHA1 0x14 0x86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA1 0x14 0x32d10c7b8cf96570ca04ce37f2a19d84240d3a89 EQUAL"], +["''", "SHA256 0x20 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 EQUAL"], +["'a'", "SHA256 0x20 0xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "SHA256 0x20 0x71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 EQUAL"], +["''", "DUP HASH160 SWAP SHA256 RIPEMD160 EQUAL"], +["''", "DUP HASH256 SWAP SHA256 SHA256 EQUAL"], +["''", "NOP HASH160 0x14 0xb472a266d0bd89c13706a4132ccfb16f7c3b9fcb EQUAL"], +["'a'", "HASH160 NOP 0x14 0x994355199e516ff76c4fa4aab39337b9d84cf12b EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH160 0x4c 0x14 0xc286a1af0947f58d1ad787385b1c2c4a976f9e71 EQUAL"], +["''", "HASH256 0x20 0x5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456 EQUAL"], +["'a'", "HASH256 0x20 0xbf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8 EQUAL"], +["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL"], + + +["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL"], +["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL"], + +["0", "IF 0xba ELSE 1 ENDIF", "opcodes above NOP10 invalid if executed"], +["0", "IF 0xbb ELSE 1 ENDIF"], +["0", "IF 0xbc ELSE 1 ENDIF"], +["0", "IF 0xbd ELSE 1 ENDIF"], +["0", "IF 0xbe ELSE 1 ENDIF"], +["0", "IF 0xbf ELSE 1 ENDIF"], +["0", "IF 0xc0 ELSE 1 ENDIF"], +["0", "IF 0xc1 ELSE 1 ENDIF"], +["0", "IF 0xc2 ELSE 1 ENDIF"], +["0", "IF 0xc3 ELSE 1 ENDIF"], +["0", "IF 0xc4 ELSE 1 ENDIF"], +["0", "IF 0xc5 ELSE 1 ENDIF"], +["0", "IF 0xc6 ELSE 1 ENDIF"], +["0", "IF 0xc7 ELSE 1 ENDIF"], +["0", "IF 0xc8 ELSE 1 ENDIF"], +["0", "IF 0xc9 ELSE 1 ENDIF"], +["0", "IF 0xca ELSE 1 ENDIF"], +["0", "IF 0xcb ELSE 1 ENDIF"], +["0", "IF 0xcc ELSE 1 ENDIF"], +["0", "IF 0xcd ELSE 1 ENDIF"], +["0", "IF 0xce ELSE 1 ENDIF"], +["0", "IF 0xcf ELSE 1 ENDIF"], +["0", "IF 0xd0 ELSE 1 ENDIF"], +["0", "IF 0xd1 ELSE 1 ENDIF"], +["0", "IF 0xd2 ELSE 1 ENDIF"], +["0", "IF 0xd3 ELSE 1 ENDIF"], +["0", "IF 0xd4 ELSE 1 ENDIF"], +["0", "IF 0xd5 ELSE 1 ENDIF"], +["0", "IF 0xd6 ELSE 1 ENDIF"], +["0", "IF 0xd7 ELSE 1 ENDIF"], +["0", "IF 0xd8 ELSE 1 ENDIF"], +["0", "IF 0xd9 ELSE 1 ENDIF"], +["0", "IF 0xda ELSE 1 ENDIF"], +["0", "IF 0xdb ELSE 1 ENDIF"], +["0", "IF 0xdc ELSE 1 ENDIF"], +["0", "IF 0xdd ELSE 1 ENDIF"], +["0", "IF 0xde ELSE 1 ENDIF"], +["0", "IF 0xdf ELSE 1 ENDIF"], +["0", "IF 0xe0 ELSE 1 ENDIF"], +["0", "IF 0xe1 ELSE 1 ENDIF"], +["0", "IF 0xe2 ELSE 1 ENDIF"], +["0", "IF 0xe3 ELSE 1 ENDIF"], +["0", "IF 0xe4 ELSE 1 ENDIF"], +["0", "IF 0xe5 ELSE 1 ENDIF"], +["0", "IF 0xe6 ELSE 1 ENDIF"], +["0", "IF 0xe7 ELSE 1 ENDIF"], +["0", "IF 0xe8 ELSE 1 ENDIF"], +["0", "IF 0xe9 ELSE 1 ENDIF"], +["0", "IF 0xea ELSE 1 ENDIF"], +["0", "IF 0xeb ELSE 1 ENDIF"], +["0", "IF 0xec ELSE 1 ENDIF"], +["0", "IF 0xed ELSE 1 ENDIF"], +["0", "IF 0xee ELSE 1 ENDIF"], +["0", "IF 0xef ELSE 1 ENDIF"], +["0", "IF 0xf0 ELSE 1 ENDIF"], +["0", "IF 0xf1 ELSE 1 ENDIF"], +["0", "IF 0xf2 ELSE 1 ENDIF"], +["0", "IF 0xf3 ELSE 1 ENDIF"], +["0", "IF 0xf4 ELSE 1 ENDIF"], +["0", "IF 0xf5 ELSE 1 ENDIF"], +["0", "IF 0xf6 ELSE 1 ENDIF"], +["0", "IF 0xf7 ELSE 1 ENDIF"], +["0", "IF 0xf8 ELSE 1 ENDIF"], +["0", "IF 0xf9 ELSE 1 ENDIF"], +["0", "IF 0xfa ELSE 1 ENDIF"], +["0", "IF 0xfb ELSE 1 ENDIF"], +["0", "IF 0xfc ELSE 1 ENDIF"], +["0", "IF 0xfd ELSE 1 ENDIF"], +["0", "IF 0xfe ELSE 1 ENDIF"], +["0", "IF 0xff ELSE 1 ENDIF"], + +["NOP", +"'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", +"520 byte push"], +["1", +"0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"201 opcodes executed. 0x61 is NOP"], +["1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1,000 stack size (0x6f is 3DUP)"], +["1 TOALTSTACK 2 TOALTSTACK 3 4 5 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1 2 3 4 5 6 7 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"1,000 stack size (altstack cleared between scriptSig/scriptPubKey)"], +["'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f", +"'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' 0x6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f 2DUP 0x616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", +"Max-size (10,000-byte), max-push(520 bytes), max-opcodes(201), max stack size(1,000 items). 0x6f is 3DUP, 0x61 is NOP"], + +["0", +"IF 0x5050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050505050 ENDIF 1", +">201 opcodes, but RESERVED (0x50) doesn't count towards opcode limit."], + +["NOP","1"], + +["1", "0x01 0x01 EQUAL", "The following is useful for checking implementations of BN_bn2mpi"], +["127", "0x01 0x7F EQUAL"], +["128", "0x02 0x8000 EQUAL", "Leave room for the sign bit"], +["32767", "0x02 0xFF7F EQUAL"], +["32768", "0x03 0x008000 EQUAL"], +["8388607", "0x03 0xFFFF7F EQUAL"], +["8388608", "0x04 0x00008000 EQUAL"], +["2147483647", "0x04 0xFFFFFF7F EQUAL"], +["2147483648", "0x05 0x0000008000 EQUAL"], +["-1", "0x01 0x81 EQUAL", "Numbers are little-endian with the MSB being a sign bit"], +["-127", "0x01 0xFF EQUAL"], +["-128", "0x02 0x8080 EQUAL"], +["-32767", "0x02 0xFFFF EQUAL"], +["-32768", "0x03 0x008080 EQUAL"], +["-8388607", "0x03 0xFFFFFF EQUAL"], +["-8388608", "0x04 0x00008080 EQUAL"], +["-2147483647", "0x04 0xFFFFFFFF EQUAL"], +["-2147483648", "0x05 0x0000008080 EQUAL"], + +["2147483647", "1ADD 2147483648 EQUAL", "We can do math on 4-byte integers, and compare 5-byte ones"], +["2147483647", "1ADD 1"], +["-2147483647", "1ADD 1"], + +["1", "0x02 0x0100 EQUAL NOT", "Not the same byte array..."], +["1", "0x02 0x0100 NUMEQUAL", "... but they are numerically equal"], +["11", "0x4c 0x03 0x0b0000 NUMEQUAL"], +["0", "0x01 0x80 EQUAL NOT"], +["0", "0x01 0x80 NUMEQUAL", "Zero numerically equals negative zero"], +["0", "0x02 0x0080 NUMEQUAL"], +["0x03 0x000080", "0x04 0x00000080 NUMEQUAL"], +["0x03 0x100080", "0x04 0x10000080 NUMEQUAL"], +["0x03 0x100000", "0x04 0x10000000 NUMEQUAL"], + +["NOP", "NOP 1", "The following tests check the if(stack.size() < N) tests in each opcode"], +["1", "IF 1 ENDIF", "They are here to catch copy-and-paste errors"], +["0", "NOTIF 1 ENDIF", "Most of them are duplicated elsewhere,"], +["1", "VERIFY 1", "but, hey, more is always better, right?"], + +["0", "TOALTSTACK 1"], +["1", "TOALTSTACK FROMALTSTACK"], +["0 0", "2DROP 1"], +["0 1", "2DUP"], +["0 0 1", "3DUP"], +["0 1 0 0", "2OVER"], +["0 1 0 0 0 0", "2ROT"], +["0 1 0 0", "2SWAP"], +["1", "IFDUP"], +["NOP", "DEPTH 1"], +["0", "DROP 1"], +["1", "DUP"], +["0 1", "NIP"], +["1 0", "OVER"], +["1 0 0 0 3", "PICK"], +["1 0", "PICK"], +["1 0 0 0 3", "ROLL"], +["1 0", "ROLL"], +["1 0 0", "ROT"], +["1 0", "SWAP"], +["0 1", "TUCK"], + +["1", "SIZE"], + +["0 0", "EQUAL"], +["0 0", "EQUALVERIFY 1"], + +["0", "1ADD"], +["2", "1SUB"], +["-1", "NEGATE"], +["-1", "ABS"], +["0", "NOT"], +["-1", "0NOTEQUAL"], + +["1 0", "ADD"], +["1 0", "SUB"], +["-1 -1", "BOOLAND"], +["-1 0", "BOOLOR"], +["0 0", "NUMEQUAL"], +["0 0", "NUMEQUALVERIFY 1"], +["-1 0", "NUMNOTEQUAL"], +["-1 0", "LESSTHAN"], +["1 0", "GREATERTHAN"], +["0 0", "LESSTHANOREQUAL"], +["0 0", "GREATERTHANOREQUAL"], +["-1 0", "MIN"], +["1 0", "MAX"], +["-1 -1 0", "WITHIN"], + +["0", "RIPEMD160"], +["0", "SHA1"], +["0", "SHA256"], +["0", "HASH160"], +["0", "HASH256"], +["NOP", "CODESEPARATOR 1"], + +["NOP", "NOP1 1"], +["NOP", "NOP2 1"], +["NOP", "NOP3 1"], +["NOP", "NOP4 1"], +["NOP", "NOP5 1"], +["NOP", "NOP6 1"], +["NOP", "NOP7 1"], +["NOP", "NOP8 1"], +["NOP", "NOP9 1"], +["NOP", "NOP10 1"], + +["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "CHECKMULTISIG is allowed to have zero keys and/or sigs"], +["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "Zero sigs means no sigs are checked"], +["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], + +["", "0 0 0 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "CHECKMULTISIG is allowed to have zero keys and/or sigs"], +["", "0 0 0 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 0 1 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "Zero sigs means no sigs are checked"], +["", "0 0 0 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], + +["", "0 0 'a' 'b' 2 CHECKMULTISIG VERIFY DEPTH 0 EQUAL", "Test from up to 20 pubkeys, all not checked"], +["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG VERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 1 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 2 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 3 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 4 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 5 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 6 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 7 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 8 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 9 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 10 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 11 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 12 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 13 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 14 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 15 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 16 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 17 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 18 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 19 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], +["", "0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY DEPTH 0 EQUAL"], + +["", +"0 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG 0 0 CHECKMULTISIG", +"nOpCount is incremented by the number of keys evaluated in addition to the usual one op per op. In this case we have zero keys, so we can execute 201 CHECKMULTISIGS"], + +["1", +"0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY 0 0 0 CHECKMULTISIGVERIFY"], + +["", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIG", +"Even though there are no signatures being checked nOpCount is incremented by the number of keys."], + +["1", +"NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY 0 0 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 20 CHECKMULTISIGVERIFY"], + +["0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL", "Very basic P2SH"], +["0x4c 0 0x01 1", "HASH160 0x14 0xda1745e9b549bd0bfa1a569971c77eba30cd5a4b EQUAL"], + +["0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"Basic PUSH signedness check"], + +["0x4c 0x40 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242", +"0x4d 0x4000 0x42424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242 EQUAL", +"Basic PUSHDATA1 signedness check"], + +["0x00", "SIZE 0 EQUAL", "Basic OP_0 execution"] +] diff --git a/bitcoin/tests/data/tx_invalid.json b/bitcoin/tests/data/tx_invalid.json new file mode 100644 index 0000000..faf40ef --- /dev/null +++ b/bitcoin/tests/data/tx_invalid.json @@ -0,0 +1,80 @@ +[ +["The following are deserialized transactions which are invalid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["serializedTransaction, enforceP2SH]"], +["Objects that are only a single string (like this one) are ignored"], + +["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"], +[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]], +"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", true], + +["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], +["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"], +["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", true], + +["Tests for CheckTransaction()"], +["No inputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"0100000000010000000000000000015100000000", true], + +["No outputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", true], + +["Negative output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xae609aca8061d77c5e111f6bb62501a6bbe2bfdb EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", true], + +["MAX_MONEY + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", true], + +["MAX_MONEY output + 1 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", true], + +["Duplicate inputs"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x236d0639db62b0773fd8ac34dc85ae19e9aba80a EQUAL"]], +"01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", true], + +["Coinbase of size 1"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", true], + +["Coinbase of size 101"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true], + +["Null txin"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "HASH160 0x14 0x02dae7dbbda56097959cba59b1989dd3e47937bf EQUAL"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6e49304602210086f39e028e46dafa8e1e3be63906465f4cf038fbe5ed6403dc3e74ae876e6431022100c4625c675cfc5c7e3a0e0d7eaec92ac24da20c73a88eb40d09253e51ac6def5201232103a183ddc41e84753aca47723c965d1b5c8b0e2b537963518355e6dd6cf8415e50acffffffff010000000000000000015100000000", true], + +["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", true], + +["Incorrect signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true], + +["Empty stack when we try to run CHECKSIG"], +[[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]], +"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", true], + +["Make diffs cleaner by leaving a comment here without comma at the end"] +] diff --git a/bitcoin/tests/data/tx_valid.json b/bitcoin/tests/data/tx_valid.json new file mode 100644 index 0000000..d2a4c9d --- /dev/null +++ b/bitcoin/tests/data/tx_valid.json @@ -0,0 +1,168 @@ +[ +["The following are deserialized transactions which are valid."], +["They are in the form"], +["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], +["serializedTransaction, enforceP2SH]"], +["Objects that are only a single string (like this one) are ignored"], + +["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"], +["See http://r6.ca/blog/20111119T211504Z.html"], +["It is also the first OP_CHECKMULTISIG transaction in standard form"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", true], + +["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], +["It has an arbitrary extra byte stuffed into the signature at pos length - 2"], +[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], +"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004A0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", true], + +["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"], +["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"], +[[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]], +"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", true], + +["A nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["Same as above, but with the signature duplicated in the scriptPubKey with the proper pushdata prefix"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", true], + +["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"], +["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"], +[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"], +["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]], +"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", true], + +["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"], +["It results in signing the constant 1, instead of something generated based on the transaction,"], +["when the input doing the signing has an index greater than the maximum output index"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]], +"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", true], + +["An invalid P2SH Transaction"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], +"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", false], + +["A valid P2SH Transaction using the standard transaction type put forth in BIP 16"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", true], + +["Tests for CheckTransaction()"], +["MAX_MONEY output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", true], + +["MAX_MONEY output + 0 output"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], +"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", true], + +["Coinbase of size 2"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", true], + +["Coinbase of size 100"], +["Note the input is just required to make the tester happy"], +[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], +"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", true], + +["Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", true], + +["Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], + ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], + "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", true], + +["afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae which has several SIGHASH_SINGLE signatures"], +[[["63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70", 0, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], + ["04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"], + ["ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"]], + "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", true], + + ["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"], + [[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]], + "0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", true], + +["Correct signature order"], +["Note the input is just required to make the tester happy"], +[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], +"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", true], + +["cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984, which is a fairly strange transaction which relies on OP_CHECKSIG returning 0 when checking a completely invalid sig of length 0"], +[[["cbebc4da731e8995fe97f6fadcd731b36ad40e5ecb31e38e904f6e5982fa09f7", 0, "0x2102085c6600657566acc2d6382a47bc3f324008d2aa10940dd7705a48aa2a5a5e33ac7c2103f5d0fb955f95dd6be6115ce85661db412ec6a08abcbfce7da0ba8297c6cc0ec4ac7c5379a820d68df9e32a147cffa36193c6f7c43a1c8c69cda530e1c6db354bfabdcfefaf3c875379a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226879a5479827701200122a59a5379827701200122a59a6353798277537982778779679a68"]], +"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", true], + +["Empty pubkey"], +[[["229257c295e7f555421c1bfec8538dd30a4b5c37c1c8810bbe83cafa7811652c", 0, "0x00 CHECKSIG NOT"]], +"01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000", true], + +["Empty signature"], +[[["9ca93cfd8e3806b9d9e2ba1cf64e3cc6946ee0119670b1796a09928d14ea25f7", 0, "0x21 0x028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02 CHECKSIG NOT"]], +"0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000", true], + +[[["444e00ed7840d41f20ecd9c11d3f91982326c731a02f3c05748414a4fa9e59be", 0, "1 0x00 0x21 0x02136b04758b0b6e363e7a6fbe83aaf527a153db2b060d36cc29f7f8309ba6e458 2 CHECKMULTISIG"]], +"0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000", true], + +[[["e16abbe80bf30c080f63830c8dbf669deaef08957446e95940227d8c5e6db612", 0, "1 0x21 0x03905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9f 0x00 2 CHECKMULTISIG"]], +"010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000", true], + +[[["ebbcf4bfce13292bd791d6a65a2a858d59adbf737e387e40370d4e64cc70efb0", 0, "2 0x21 0x033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194 0x21 0x03a88b326f8767f4f192ce252afe33c94d25ab1d24f27f159b3cb3aa691ffe1423 2 CHECKMULTISIG NOT"]], +"0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000", true], + +[[["ba4cd7ae2ad4d4d13ebfc8ab1d93a63e4a6563f25089a18bf0fc68f282aa88c1", 0, "2 0x21 0x037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1 0x21 0x02edc823cd634f2c4033d94f5755207cb6b60c4b1f1f056ad7471c47de5f2e4d50 2 CHECKMULTISIG NOT"]], +"0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000", true], + + +["OP_CODESEPARATOR tests"], + +["Test that SignatureHash() removes OP_CODESEPARATOR with FindAndDelete()"], +[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], +"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", true], +[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], +"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", true], + +["Hashed data starts at the CODESEPARATOR"], +[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]], +"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", true], + +["But only if execution has reached it"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], +"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", true], + +["CHECKSIG is legal in scriptSigs"], +[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", true], + +["Same semantics for OP_CODESEPARATOR"], +[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", true], + +["Signatures are removed from the script they are in by FindAndDelete() in the CHECKSIG code; even multiple instances of one signature can be removed."], +[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", true], + +["That also includes ahead of the opcode being executed."], +[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], +"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", true], + +["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."], + +["Here's an example on mainnet within a P2SH redeemScript. Remarkably it's a standard transaction in <0.9"], +[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]], +"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", true], + +["Same idea, but with bare CHECKMULTISIG"], +[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], +"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", true], + + +["Make diffs cleaner by leaving a comment here without comma at the end"] +] diff --git a/bitcoin/tests/test_base58.py b/bitcoin/tests/test_base58.py index 2acf72d..a57d1fe 100644 --- a/bitcoin/tests/test_base58.py +++ b/bitcoin/tests/test_base58.py @@ -1,5 +1,13 @@ -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. from __future__ import absolute_import, division, print_function, unicode_literals @@ -12,14 +20,14 @@ from bitcoin.base58 import * -def load_test_vector(name): +def load_test_vectors(name): with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: for testcase in json.load(fd): yield testcase class Test_base58(unittest.TestCase): def test_encode_decode(self): - for exp_bin, exp_base58 in load_test_vector('base58_encode_decode.json'): + for exp_bin, exp_base58 in load_test_vectors('base58_encode_decode.json'): exp_bin = unhexlify(exp_bin.encode('utf8')) act_base58 = encode(exp_bin) @@ -28,8 +36,23 @@ def test_encode_decode(self): self.assertEqual(act_base58, exp_base58) self.assertEqual(act_bin, exp_bin) - def test_invalid_base58_exception(self): - with self.assertRaises(InvalidBase58Error): - decode('#') +class Test_CBase58Data(unittest.TestCase): + def test_from_data(self): + b = CBase58Data.from_bytes(b"b\xe9\x07\xb1\\\xbf'\xd5BS\x99\xeb\xf6\xf0\xfbP\xeb\xb8\x8f\x18", 0) + self.assertEqual(b.nVersion, 0) + self.assertEqual(str(b), '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa') + + b = CBase58Data.from_bytes(b'Bf\xfco,(a\xd7\xfe"\x9b\'\x9ay\x80:\xfc\xa7\xba4', 196) + self.assertEqual(b.nVersion, 196) + self.assertEqual(str(b), '2MyJKxYR2zNZZsZ39SgkCXWCfQtXKhnWSWq') - # FIXME: need to test CBitcoinAddress + def test_invalid_base58_exception(self): + invalids = ('', # missing everything + '#', # invalid character + '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNb', # invalid checksum + ) + + for invalid in invalids: + msg = '%r should have raised InvalidBase58Error but did not' % invalid + with self.assertRaises(Base58Error, msg=msg): + CBase58Data(invalid) diff --git a/bitcoin/tests/test_bloom.py b/bitcoin/tests/test_bloom.py index 5e4b477..2549da3 100644 --- a/bitcoin/tests/test_bloom.py +++ b/bitcoin/tests/test_bloom.py @@ -1,5 +1,13 @@ -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. from __future__ import absolute_import, division, print_function, unicode_literals @@ -7,31 +15,54 @@ import os import unittest -from binascii import unhexlify - +import bitcoin.core +from bitcoin.core import x from bitcoin.bloom import * +class Test_MurmurHash3(unittest.TestCase): + def test(self): + def T(expected, seed, data): + self.assertEqual(MurmurHash3(seed, x(data)), expected) + + T(0x00000000, 0x00000000, "") + T(0x6a396f08, 0xFBA4C795, "") + T(0x81f16f39, 0xffffffff, "") + + T(0x514e28b7, 0x00000000, "00") + T(0xea3f0b17, 0xFBA4C795, "00") + T(0xfd6cf10d, 0x00000000, "ff") + + T(0x16c6b7ab, 0x00000000, "0011") + T(0x8eb51c3d, 0x00000000, "001122") + T(0xb4471bf8, 0x00000000, "00112233") + T(0xe2301fa8, 0x00000000, "0011223344") + T(0xfc2e4a15, 0x00000000, "001122334455") + T(0xb074502c, 0x00000000, "00112233445566") + T(0x8034d2a0, 0x00000000, "0011223344556677") + T(0xb4698def, 0x00000000, "001122334455667788") + + class Test_CBloomFilter(unittest.TestCase): def test_create_insert_serialize(self): filter = CBloomFilter(3, 0.01, 0, CBloomFilter.UPDATE_ALL) def T(elem): """Filter contains elem""" - elem = unhexlify(elem) + elem = x(elem) filter.insert(elem) self.assertTrue(filter.contains(elem)) def F(elem): """Filter does not contain elem""" - elem = unhexlify(elem) + elem = x(elem) self.assertFalse(filter.contains(elem)) - T(b'99108ad8ed9bb6274d3980bab5a85c048f0950c8') - F(b'19108ad8ed9bb6274d3980bab5a85c048f0950c8') - T(b'b5a2c786d9ef4658287ced5914b37a1b4aa32eee') - T(b'b9300670b4c5366e95b2699e8b18bc75e5f729c5') + T('99108ad8ed9bb6274d3980bab5a85c048f0950c8') + F('19108ad8ed9bb6274d3980bab5a85c048f0950c8') + T('b5a2c786d9ef4658287ced5914b37a1b4aa32eee') + T('b9300670b4c5366e95b2699e8b18bc75e5f729c5') - self.assertEqual(filter.serialize(), unhexlify(b'03614e9b050000000000000001')) + self.assertEqual(filter.serialize(), x('03614e9b050000000000000001')) def test_create_insert_serialize_with_tweak(self): # Same test as bloom_create_insert_serialize, but we add a nTweak of 100 @@ -39,29 +70,29 @@ def test_create_insert_serialize_with_tweak(self): def T(elem): """Filter contains elem""" - elem = unhexlify(elem) + elem = x(elem) filter.insert(elem) self.assertTrue(filter.contains(elem)) def F(elem): """Filter does not contain elem""" - elem = unhexlify(elem) + elem = x(elem) self.assertFalse(filter.contains(elem)) - T(b'99108ad8ed9bb6274d3980bab5a85c048f0950c8') - F(b'19108ad8ed9bb6274d3980bab5a85c048f0950c8') - T(b'b5a2c786d9ef4658287ced5914b37a1b4aa32eee') - T(b'b9300670b4c5366e95b2699e8b18bc75e5f729c5') + T('99108ad8ed9bb6274d3980bab5a85c048f0950c8') + F('19108ad8ed9bb6274d3980bab5a85c048f0950c8') + T('b5a2c786d9ef4658287ced5914b37a1b4aa32eee') + T('b9300670b4c5366e95b2699e8b18bc75e5f729c5') - self.assertEqual(filter.serialize(), unhexlify(b'03ce4299050000000100008001')) + self.assertEqual(filter.serialize(), x('03ce4299050000000100008001')) def test_bloom_create_insert_key(self): filter = CBloomFilter(2, 0.001, 0, CBloomFilter.UPDATE_ALL) - pubkey = unhexlify(b'045B81F0017E2091E2EDCD5EECF10D5BDD120A5514CB3EE65B8447EC18BFC4575C6D5BF415E54E03B1067934A0F0BA76B01C6B9AB227142EE1D543764B69D901E0') - pubkeyhash = ser_uint160(Hash160(pubkey)) + pubkey = x('045B81F0017E2091E2EDCD5EECF10D5BDD120A5514CB3EE65B8447EC18BFC4575C6D5BF415E54E03B1067934A0F0BA76B01C6B9AB227142EE1D543764B69D901E0') + pubkeyhash = bitcoin.core.Hash160(pubkey) filter.insert(pubkey) filter.insert(pubkeyhash) - self.assertEqual(filter.serialize(), unhexlify(b'038fc16b080000000000000001')) + self.assertEqual(filter.serialize(), x('038fc16b080000000000000001')) diff --git a/bitcoin/tests/test_checkblock.py b/bitcoin/tests/test_checkblock.py new file mode 100644 index 0000000..a011fe2 --- /dev/null +++ b/bitcoin/tests/test_checkblock.py @@ -0,0 +1,60 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import json +import unittest +import os + +from bitcoin.core import * + +def load_test_vectors(name): + with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: + for test_case in json.load(fd): + # Comments designated by single length strings + if len(test_case) == 1: + continue + assert len(test_case) == 5 + + (comment, fHeader, fCheckPoW, cur_time, serialized_blk) = test_case + + blk = None + if fHeader: + blk = CBlockHeader.deserialize(x(serialized_blk)) + else: + blk = CBlock.deserialize(x(serialized_blk)) + + yield (comment, fHeader, fCheckPoW, cur_time, blk) + + +class Test_CheckBlock(unittest.TestCase): + def test_checkblock_valid(self): + for comment, fHeader, fCheckPoW, cur_time, blk in load_test_vectors('checkblock_valid.json'): + try: + if fHeader: + CheckBlockHeader(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) + else: + CheckBlock(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) + except ValidationError as err: + self.fail('Failed "%s" with error %r' % (comment, err)) + + def test_checkblock_invalid(self): + for comment, fHeader, fCheckPoW, cur_time, blk in load_test_vectors('checkblock_invalid.json'): + try: + if fHeader: + CheckBlockHeader(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) + else: + CheckBlock(blk, fCheckPoW=fCheckPoW, cur_time=cur_time) + except ValidationError as err: + continue + + self.fail('Invalid block "%s" passed checks' % comment) diff --git a/bitcoin/tests/test_core.py b/bitcoin/tests/test_core.py new file mode 100644 index 0000000..1d612af --- /dev/null +++ b/bitcoin/tests/test_core.py @@ -0,0 +1,122 @@ +# Copyright (C) 2013-2015 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import unittest + +from bitcoin.core import * + +class Test_str_value(unittest.TestCase): + def test(self): + def T(value, expected): + actual = str_money_value(value) + self.assertEqual(actual, expected) + + T( 0, '0.0') + T( 1, '0.00000001') + T( 10, '0.0000001') + T( 12345678, '0.12345678') + T( 10000000, '0.1') + T( 100000000, '1.0') + T(1000000000, '10.0') + T(1010000000, '10.1') + T(1001000000, '10.01') + T(1012345678, '10.12345678') + +class Test_Money(unittest.TestCase): + def test_MoneyRange(self): + self.assertFalse(MoneyRange(-1)) + self.assertTrue(MoneyRange(0)) + self.assertTrue(MoneyRange(100000)) + self.assertTrue(MoneyRange(21000000 * COIN)) # Maximum money on Bitcoin network + self.assertFalse(MoneyRange(21000001 * COIN)) + + def test_MoneyRangeCustomParams(self): + highMaxParamsType = type(str('CoreHighMainParams'), (CoreMainParams,object), {'MAX_MONEY': 22000000 * COIN }) + highMaxParams = highMaxParamsType() + self.assertTrue(MoneyRange(21000001 * COIN, highMaxParams)) + self.assertTrue(MoneyRange(22000000 * COIN, highMaxParams)) + self.assertFalse(MoneyRange(22000001 * COIN, highMaxParams)) + +class Test_CBlockHeader(unittest.TestCase): + def test_serialization(self): + genesis = CBlockHeader(nVersion=1, + hashPrevBlock=lx('0000000000000000000000000000000000000000000000000000000000000000'), + hashMerkleRoot=lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), + nTime=1231006505, + nBits=486604799, + nNonce=2083236893) + serialized = genesis.serialize() + self.assertEqual(Hash(serialized), + lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) + + genesis2 = CBlockHeader.deserialize(serialized) + self.assertEqual(genesis, genesis2) + + def test_GetHash(self): + genesis = CBlockHeader(nVersion=1, + hashPrevBlock=lx('0000000000000000000000000000000000000000000000000000000000000000'), + hashMerkleRoot=lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), + nTime=1231006505, + nBits=486604799, + nNonce=2083236893) + self.assertEqual(genesis.GetHash(), lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) + + def test_calc_difficulty(self): + def T(nbits, expected): + actual = CBlockHeader.calc_difficulty(nbits) + actual = round(actual, 3) + self.assertEqual(actual, expected) + + T(486604799, 1.000) # block 0 + T(486594666, 1.183) # block 33333 + T(469809688, 352.161) # block 74000 + T(453179945, 22012.381) # block 105000 + T(436527338, 3438908.960) # block 210000 + T(426957810, 37392766.136) # block 250000 + +class Test_CBlock(unittest.TestCase): + def test_serialization(self): + initial_serialized = x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000') + genesis = CBlock.deserialize(initial_serialized) + serialized = genesis.serialize() + self.assertEqual(Hash(serialized[0:80]), + lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) + self.assertEqual(serialized, initial_serialized) + + def test_GetHash(self): + genesis = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) + self.assertEqual(genesis.GetHash(), lx('000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f')) + + def test_calc_merkle_root_of_empty_block(self): + """CBlock.calc_merkle_root() fails if vtx empty""" + block = CBlock() + with self.assertRaises(ValueError): + block.calc_merkle_root() + + def test_calc_merkle_root(self): + # genesis + block = CBlock.deserialize(x('0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000')) + self.assertEqual(block.calc_merkle_root(), lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b')) + self.assertEqual(block.vMerkleTree, (lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'),)) + + # 170 two transactions + block = CBlock.deserialize(x('0100000055bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000ff104ccb05421ab93e63f8c3ce5c2c2e9dbb37de2764b3a3175c8166562cac7d51b96a49ffff001d283e9e700201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0102ffffffff0100f2052a01000000434104d46c4968bde02899d2aa0963367c7a6ce34eec332b32e42e5f3407e052d64ac625da6f0718e7b302140434bd725706957c092db53805b821a85b23a7ac61725bac000000000100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000')) + self.assertEqual(block.calc_merkle_root(), lx('7dac2c5666815c17a3b36427de37bb9d2e2c5ccec3f8633eb91a4205cb4c10ff')) + + # 99960 three transactions + block = CBlock.deserialize(x('01000000e78b20013e6e9a21b6366ead5d866b2f9dc00664508b90f24da8000000000000f94b61259c7e9af3455b277275800d0d6a58b929eedf9e0153a6ef2278a5d53408d11a4d4c86041b0fbf10b00301000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0119ffffffff0100f2052a0100000043410427e729f9cb5564abf2a1ccda596c636b77bd4d9d91f657d4738f3c70fce8ac4e12b1c782905554d9ff2c2e050fdfe3ff93c91c5817e617877d51f450b528c9e4ac000000000100000001e853c9e0c133547fd9e162b1d3860dd0f27d5b9b8a7430d28896c00fbb3f1bc7000000008c49304602210095bcd54ebd0caa7cee75f0f89de472a765e6ef4b98c5fd4b32c7f9d4905db9ae022100ebd3f668e3a1a36d56e30184c27531dbb9fc136c84b1282be562064d86997d1e014104727eb4fdcc90658cd26abe7dcb0ae7297810b15b9e27c32bcf8e3edd934901968806dc18b1276d7273cc4c223feee0070361ed947888a3cef422bebfede96e08ffffffff020065cd1d000000001976a91468c6c2b3c0bc4a8eeb10d16a300d627a31a3b58588ac0008af2f000000001976a9141d87f0a54a1d704ffc70eae83b025698bc0fdcfc88ac00000000010000000125f582f1d37b6713b14b85665a2daea4f464f5ed1c3ab3d4dcf152fb61414b9e000000008a473044022066ec12ced31659e1bf961b542b58bba76ba8f2a1e8f36d5f60be0601598eac21022047ce33685a63283a4c3ebc390261191f215999b2f7d8e1504b8af39aae4a2881014104c5e1d713d10fe59cc48f60701a3efcac418969c22e9c6cf57440f71e44dc82837af5351bf3e1d898f06aa5c792bf0251a39902311d1d27c16847b1b414494f35ffffffff02404b4c00000000001976a91466a3b2e43cfa5c6d9b2f0095f7be5a5cb608478c88ac80b8dc3c030000001976a9146df5ed8cee34df5c05c90406761a11ed143c202d88ac00000000')) + self.assertEqual(block.calc_merkle_root(), lx('34d5a57822efa653019edfee29b9586a0d0d807572275b45f39a7e9c25614bf9')) + + # 99993 four transactions + block = CBlock.deserialize(x('01000000acda3db591d5c2c63e8c09e7523a5b0581707ef3e3520d6ca180000000000000701179cb9a9e0fe709cc96261b6b943b31362b61dacba94b03f9b71a06cc2eff7d1c1b4d4c86041b75962f880401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0152ffffffff014034152a01000000434104216220ab283b5e2871c332de670d163fb1b7e509fd67db77997c5568e7c25afd988f19cd5cc5aec6430866ec64b5214826b28e0f7a86458073ff933994b47a5cac0000000001000000042a40ae58b06c3a61ae55dbee05cab546e80c508f71f24ef0cdc9749dac91ea5f000000004a49304602210089c685b37903c4aa62d984929afeaca554d1641f9a668398cd228fb54588f06b0221008a5cfbc5b0a38ba78c4f4341e53272b9cd0e377b2fb740106009b8d7fa693f0b01ffffffff7b999491e30af112b11105cb053bc3633a8a87f44740eb158849a76891ff228b00000000494830450221009a4aa8663ff4017063d2020519f2eade5b4e3e30be69bf9a62b4e6472d1747b2022021ee3b3090b8ce439dbf08a5df31e2dc23d68073ebda45dc573e8a4f74f5cdfc01ffffffffdea82ec2f9e88e0241faa676c13d093030b17c479770c6cc83239436a4327d49000000004a493046022100c29d9de71a34707c52578e355fa0fdc2bb69ce0a957e6b591658a02b1e039d69022100f82c8af79c166a822d305f0832fb800786d831aea419069b3aed97a6edf8f02101fffffffff3e7987da9981c2ae099f97a551783e1b21669ba0bf3aca8fe12896add91a11a0000000049483045022100e332c81781b281a3b35cf75a5a204a2be451746dad8147831255291ebac2604d02205f889a2935270d1bf1ef47db773d68c4d5c6a51bb51f082d3e1c491de63c345601ffffffff0100c817a8040000001976a91420420e56079150b50fb0617dce4c374bd61eccea88ac00000000010000000265a7293b2d69ba51d554cd32ac7586f7fbeaeea06835f26e03a2feab6aec375f000000004a493046022100922361eaafe316003087d355dd3c0ef3d9f44edae661c212a28a91e020408008022100c9b9c84d53d82c0ba9208f695c79eb42a453faea4d19706a8440e1d05e6cff7501fffffffff6971f00725d17c1c531088144b45ed795a307a22d51ca377c6f7f93675bb03a000000008b483045022100d060f2b2f4122edac61a25ea06396fe9135affdabc66d350b5ae1813bc6bf3f302205d8363deef2101fc9f3d528a8b3907e9d29c40772e587dcea12838c574cb80f801410449fce4a25c972a43a6bc67456407a0d4ced782d4cf8c0a35a130d5f65f0561e9f35198349a7c0b4ec79a15fead66bd7642f17cc8c40c5df95f15ac7190c76442ffffffff0200f2052a010000001976a914c3f537bc307c7eda43d86b55695e46047b770ea388ac00cf7b05000000001976a91407bef290008c089a60321b21b1df2d7f2202f40388ac0000000001000000014ab7418ecda2b2531eef0145d4644a4c82a7da1edd285d1aab1ec0595ac06b69000000008c493046022100a796490f89e0ef0326e8460edebff9161da19c36e00c7408608135f72ef0e03e0221009e01ef7bc17cddce8dfda1f1a6d3805c51f9ab2f8f2145793d8e85e0dd6e55300141043e6d26812f24a5a9485c9d40b8712215f0c3a37b0334d76b2c24fcafa587ae5258853b6f49ceeb29cd13ebb76aa79099fad84f516bbba47bd170576b121052f1ffffffff0200a24a04000000001976a9143542e17b6229a25d5b76909f9d28dd6ed9295b2088ac003fab01000000001976a9149cea2b6e3e64ad982c99ebba56a882b9e8a816fe88ac00000000')) + self.assertEqual(block.calc_merkle_root(), lx('ff2ecc061ab7f9034ba9cbda612b36313b946b1b2696cc09e70f9e9acb791170')) diff --git a/bitcoin/tests/test_hash.py b/bitcoin/tests/test_hash.py deleted file mode 100644 index 500216b..0000000 --- a/bitcoin/tests/test_hash.py +++ /dev/null @@ -1,34 +0,0 @@ -# Distributed under the MIT/X11 software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from __future__ import absolute_import, division, print_function, unicode_literals - -import json -import os -import unittest - -from binascii import unhexlify - -from bitcoin.hash import * - -class Test_MurmurHash3(unittest.TestCase): - def test(self): - def T(expected, seed, data): - self.assertEqual(MurmurHash3(seed, unhexlify(data)), expected) - - T(0x00000000, 0x00000000, b""); - T(0x6a396f08, 0xFBA4C795, b""); - T(0x81f16f39, 0xffffffff, b""); - - T(0x514e28b7, 0x00000000, b"00"); - T(0xea3f0b17, 0xFBA4C795, b"00"); - T(0xfd6cf10d, 0x00000000, b"ff"); - - T(0x16c6b7ab, 0x00000000, b"0011"); - T(0x8eb51c3d, 0x00000000, b"001122"); - T(0xb4471bf8, 0x00000000, b"00112233"); - T(0xe2301fa8, 0x00000000, b"0011223344"); - T(0xfc2e4a15, 0x00000000, b"001122334455"); - T(0xb074502c, 0x00000000, b"00112233445566"); - T(0x8034d2a0, 0x00000000, b"0011223344556677"); - T(0xb4698def, 0x00000000, b"001122334455667788"); diff --git a/bitcoin/tests/test_key.py b/bitcoin/tests/test_key.py new file mode 100644 index 0000000..ead3c11 --- /dev/null +++ b/bitcoin/tests/test_key.py @@ -0,0 +1,41 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import unittest + +from bitcoin.core.key import * +from bitcoin.core import x, b2x + +class Test_CPubKey(unittest.TestCase): + def test(self): + def T(hex_pubkey, is_valid, is_fullyvalid, is_compressed): + key = CPubKey(x(hex_pubkey)) + self.assertEqual(key.is_valid, is_valid) + self.assertEqual(key.is_fullyvalid, is_fullyvalid) + self.assertEqual(key.is_compressed, is_compressed) + + T('', False, False, False) + T('00', True, True, False) # why is this valid? + T('01', True, False, False) + T('02', True, False, False) + + T('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', + True, True, True) + T('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', + True, False, True) + + T('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', + True, True, True) + + T('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455', + True, True, False) diff --git a/bitcoin/tests/test_messages.py b/bitcoin/tests/test_messages.py new file mode 100644 index 0000000..8b51a12 --- /dev/null +++ b/bitcoin/tests/test_messages.py @@ -0,0 +1,137 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +import unittest + +from bitcoin.messages import msg_version, msg_verack, msg_addr, msg_alert, \ + msg_inv, msg_getdata, msg_getblocks, msg_getheaders, msg_headers, msg_tx, \ + msg_block, msg_getaddr, msg_ping, msg_pong, msg_mempool, MsgSerializable, \ + msg_notfound, msg_reject + +import sys +if sys.version > '3': + from io import BytesIO +else: + from cStringIO import StringIO as BytesIO + + +class MessageTestCase(unittest.TestCase): + def serialization_test(self, cls): + m = cls() + mSerialized = m.to_bytes() + mDeserialzed = cls.from_bytes(mSerialized) + mSerialzedTwice = mDeserialzed.to_bytes() + self.assertEqual(mSerialized, mSerialzedTwice) + + +class Test_msg_version(MessageTestCase): + def test_serialization(self): + super(Test_msg_version, self).serialization_test(msg_version) + + +class Test_msg_verack(MessageTestCase): + def test_serialization(self): + super(Test_msg_verack, self).serialization_test(msg_verack) + + +class Test_msg_addr(MessageTestCase): + def test_serialization(self): + super(Test_msg_addr, self).serialization_test(msg_addr) + + +class Test_msg_alert(MessageTestCase): + def test_serialization(self): + super(Test_msg_alert, self).serialization_test(msg_alert) + + +class Test_msg_inv(MessageTestCase): + def test_serialization(self): + super(Test_msg_inv, self).serialization_test(msg_inv) + + +class Test_msg_getdata(MessageTestCase): + def test_serialization(self): + super(Test_msg_getdata, self).serialization_test(msg_getdata) + + +class Test_msg_getblocks(MessageTestCase): + def test_serialization(self): + super(Test_msg_getblocks, self).serialization_test(msg_getblocks) + + +class Test_msg_notfound(MessageTestCase): + def test_serialization(self): + super(Test_msg_notfound, self).serialization_test(msg_notfound) + + +class Test_msg_getheaders(MessageTestCase): + def test_serialization(self): + super(Test_msg_getheaders, self).serialization_test(msg_getheaders) + + +class Test_msg_headers(MessageTestCase): + def test_serialization(self): + super(Test_msg_headers, self).serialization_test(msg_headers) + + +class Test_msg_tx(MessageTestCase): + def test_serialization(self): + super(Test_msg_tx, self).serialization_test(msg_tx) + + +class Test_msg_block(MessageTestCase): + def test_serialization(self): + super(Test_msg_block, self).serialization_test(msg_block) + + +class Test_msg_getaddr(MessageTestCase): + def test_serialization(self): + super(Test_msg_getaddr, self).serialization_test(msg_getaddr) + + +class Test_msg_ping(MessageTestCase): + def test_serialization(self): + super(Test_msg_ping, self).serialization_test(msg_ping) + + +class Test_msg_pong(MessageTestCase): + def test_serialization(self): + super(Test_msg_pong, self).serialization_test(msg_pong) + + +class Test_msg_reject(MessageTestCase): + def test_serialization(self): + super(Test_msg_reject, self).serialization_test(msg_reject) + + +class Test_msg_mempool(MessageTestCase): + def test_serialization(self): + super(Test_msg_mempool, self).serialization_test(msg_mempool) + + +class Test_messages(unittest.TestCase): + verackbytes = b'\xf9\xbe\xb4\xd9verack\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00]\xf6\xe0\xe2' + + def test_read_msg_verack(self): + f = BytesIO(self.verackbytes) + m = MsgSerializable.stream_deserialize(f) + self.assertEqual(m.command, msg_verack.command) + + def test_fail_invalid_message(self): + bad_verack_bytes = b'\xf8' + self.verackbytes[1:] + f = BytesIO(bad_verack_bytes) + with self.assertRaises(ValueError): + MsgSerializable.stream_deserialize(f) + + def test_msg_verack_to_bytes(self): + m = msg_verack() + b = m.to_bytes() + self.assertEqual(self.verackbytes, b) diff --git a/bitcoin/tests/test_net.py b/bitcoin/tests/test_net.py new file mode 100644 index 0000000..1bc361b --- /dev/null +++ b/bitcoin/tests/test_net.py @@ -0,0 +1,76 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +import unittest + +from bitcoin.net import CAddress + +class Test_CAddress(unittest.TestCase): + def test_serializationSimple(self): + c = CAddress() + cSerialized = c.serialize() + cDeserialized = CAddress.deserialize(cSerialized) + cSerializedTwice = cDeserialized.serialize() + self.assertEqual(cSerialized, cSerializedTwice) + + def test_serializationIPv4(self): + c = CAddress() + c.ip = "1.1.1.1" + c.port = 8333 + c.nTime = 1420576401 + + cSerialized = c.serialize() + cDeserialized = CAddress.deserialize(cSerialized) + + self.assertEqual(c, cDeserialized) + + cSerializedTwice = cDeserialized.serialize() + self.assertEqual(cSerialized, cSerializedTwice) + + + def test_serializationIPv6(self): + c = CAddress() + c.ip = "1234:ABCD:1234:ABCD:1234:00:ABCD:1234" + c.port = 8333 + c.nTime = 1420576401 + + cSerialized = c.serialize() + cDeserialized = CAddress.deserialize(cSerialized) + + self.assertEqual(c, cDeserialized) + + cSerializedTwice = cDeserialized.serialize() + self.assertEqual(cSerialized, cSerializedTwice) + + + def test_serializationDiff(self): + # Sanity check that the serialization code preserves differences + c1 = CAddress() + c1.ip = "1.1.1.1" + c1.port = 8333 + c1.nTime = 1420576401 + + c2 = CAddress() + c2.ip = "1.1.1.2" + c2.port = 8333 + c2.nTime = 1420576401 + + self.assertNotEqual(c1, c2) + + c1Serialized = c1.serialize() + c2Serialized = c2.serialize() + + self.assertNotEqual(c1Serialized, c2Serialized) + + c1Deserialized = CAddress.deserialize(c1Serialized) + c2Deserialized = CAddress.deserialize(c2Serialized) + + self.assertNotEqual(c1Deserialized, c2Deserialized) diff --git a/bitcoin/tests/test_rpc.py b/bitcoin/tests/test_rpc.py new file mode 100644 index 0000000..07d1f96 --- /dev/null +++ b/bitcoin/tests/test_rpc.py @@ -0,0 +1,36 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import unittest + +from bitcoin.rpc import Proxy + +class Test_RPC(unittest.TestCase): + # Tests disabled, see discussion below. + # "Looks like your unit tests won't work if Bitcoin Core isn't running; + # maybe they in turn need to check that and disable the test if core isn't available?" + # https://github.com/petertodd/python-bitcoinlib/pull/10 + pass + +# def test_can_validate(self): +# working_address = '1CB2fxLGAZEzgaY4pjr4ndeDWJiz3D3AT7' +# p = Proxy() +# r = p.validateAddress(working_address) +# self.assertEqual(r['address'], working_address) +# self.assertEqual(r['isvalid'], True) +# +# def test_cannot_validate(self): +# non_working_address = 'LTatMHrYyHcxhxrY27AqFN53bT4TauR86h' +# p = Proxy() +# r = p.validateAddress(non_working_address) +# self.assertEqual(r['isvalid'], False) diff --git a/bitcoin/tests/test_script.py b/bitcoin/tests/test_script.py new file mode 100644 index 0000000..4a895b3 --- /dev/null +++ b/bitcoin/tests/test_script.py @@ -0,0 +1,436 @@ +# Copyright (C) 2013-2015 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import unittest +import os + +from bitcoin.core import b2x,x +from bitcoin.core.script import * + +class Test_CScriptOp(unittest.TestCase): + def test_pushdata(self): + def T(data, expected): + data = x(data) + expected = x(expected) + serialized_data = CScriptOp.encode_op_pushdata(data) + self.assertEqual(serialized_data, expected) + + T('', '00') + T('00', '0100') + T('0011223344556677', '080011223344556677') + T('ff'*0x4b, '4b' + 'ff'*0x4b) + T('ff'*0x4c, '4c4c' + 'ff'*0x4c) + T('ff'*0x4c, '4c4c' + 'ff'*0x4c) + T('ff'*0xff, '4cff' + 'ff'*0xff) + T('ff'*0x100, '4d0001' + 'ff'*0x100) + T('ff'*0xffff, '4dffff' + 'ff'*0xffff) + T('ff'*0x10000, '4e00000100' + 'ff'*0x10000) + + def test_is_singleton(self): + self.assertTrue(OP_0 is CScriptOp(0x00)) + self.assertTrue(OP_1 is CScriptOp(0x51)) + self.assertTrue(OP_16 is CScriptOp(0x60)) + self.assertTrue(OP_CHECKSIG is CScriptOp(0xac)) + + for i in range(0x0, 0x100): + self.assertTrue(CScriptOp(i) is CScriptOp(i)) + + def test_encode_decode_op_n(self): + def t(n, op): + actual = CScriptOp.encode_op_n(n) + self.assertEqual(actual, op) + self.assertTrue(isinstance(actual, CScriptOp)) + + actual = op.decode_op_n() + self.assertEqual(actual, n) + self.assertTrue(isinstance(actual, int)) + + t(0, OP_0) + t(1, OP_1) + t(2, OP_2) + t(3, OP_3) + t(4, OP_4) + t(5, OP_5) + t(6, OP_6) + t(7, OP_7) + t(8, OP_8) + t(9, OP_9) + t(9, OP_9) + t(10, OP_10) + t(11, OP_11) + t(12, OP_12) + t(13, OP_13) + t(14, OP_14) + t(15, OP_15) + t(16, OP_16) + + with self.assertRaises(ValueError): + OP_CHECKSIG.decode_op_n() + + with self.assertRaises(ValueError): + CScriptOp(1).decode_op_n() + +class Test_CScript(unittest.TestCase): + def test_tokenize_roundtrip(self): + def T(serialized_script, expected_tokens, test_roundtrip=True): + serialized_script = x(serialized_script) + script_obj = CScript(serialized_script) + actual_tokens = list(script_obj) + self.assertEqual(actual_tokens, expected_tokens) + + if test_roundtrip: + recreated_script = CScript(actual_tokens) + self.assertEqual(recreated_script, serialized_script) + + T('', []) + + # standard pushdata + T('00', [b'']) + T('0100', [b'\x00']) + T('4b' + 'ff'*0x4b, [b'\xff'*0x4b]) + + # non-optimal pushdata + T('4c00', [b''], False) + T('4c04deadbeef', [x('deadbeef')], False) + T('4d0000', [b''], False) + T('4d0400deadbeef', [x('deadbeef')], False) + T('4e00000000', [b''], False) + T('4e04000000deadbeef', [x('deadbeef')], False) + + # numbers + T('4f', [OP_1NEGATE]) + T('51', [0x1]) + T('52', [0x2]) + T('53', [0x3]) + T('54', [0x4]) + T('55', [0x5]) + T('56', [0x6]) + T('57', [0x7]) + T('58', [0x8]) + T('59', [0x9]) + T('5a', [0xa]) + T('5b', [0xb]) + T('5c', [0xc]) + T('5d', [0xd]) + T('5e', [0xe]) + T('5f', [0xf]) + + # some opcodes + T('9b', [OP_BOOLOR]) + T('9a9b', [OP_BOOLAND, OP_BOOLOR]) + T('ff', [OP_INVALIDOPCODE]) + T('fafbfcfd', [CScriptOp(0xfa), CScriptOp(0xfb), CScriptOp(0xfc), CScriptOp(0xfd)]) + + # all three types + T('512103e2a0e6a91fa985ce4dda7f048fca5ec8264292aed9290594321aa53d37fdea32410478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc345552ae', + [1, + x('03e2a0e6a91fa985ce4dda7f048fca5ec8264292aed9290594321aa53d37fdea32'), + x('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455'), + 2, + OP_CHECKMULTISIG]) + + def test_invalid_scripts(self): + def T(serialized): + with self.assertRaises(CScriptInvalidError): + list(CScript(x(serialized))) + + T('01') + T('02') + T('0201') + T('4b') + T('4b' + 'ff'*0x4a) + T('4c') + T('4cff' + 'ff'*0xfe) + T('4d') + T('4dff') + T('4dffff' + 'ff'*0xfffe) + T('4e') + T('4effffff') + T('4effffffff' + 'ff'*0xfffe) # not going to test with 4GiB-1... + + def test_equality(self): + # Equality is on the serialized script, not the logical meaning. + # This is important for P2SH. + def T(serialized1, serialized2, are_equal): + script1 = CScript(x(serialized1)) + script2 = CScript(x(serialized2)) + if are_equal: + self.assertEqual(script1, script2) + else: + self.assertNotEqual(script1, script2) + + T('', '', True) + T('', '00', False) + T('00', '00', True) + T('00', '01', False) + T('01ff', '01ff', True) + T('fc01ff', '01ff', False) + + # testing equality on an invalid script is legal, and evaluates based + # on the serialization + T('4e', '4e', True) + T('4e', '4e00', False) + + def test_add(self): + script = CScript() + script2 = script + 1 + + # + operator must create a new instance + self.assertIsNot(script, script2) + + script = script2 + self.assertEqual(script, b'\x51') + + script += 2 + # += should not be done in place + self.assertIsNot(script, script2) + self.assertEqual(script, b'\x51\x52') + + script += OP_CHECKSIG + self.assertEqual(script, b'\x51\x52\xac') + + script += b'deadbeef' + self.assertEqual(script, b'\x51\x52\xac\x08deadbeef') + + script = CScript() + 1 + 2 + OP_CHECKSIG + b'deadbeef' + self.assertEqual(script, b'\x51\x52\xac\x08deadbeef') + + # big number + script = CScript() + 2**64 + self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') + + # some stuff we can't add + with self.assertRaises(TypeError): + script += None + self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') + + with self.assertRaises(TypeError): + script += [1, 2, 3] + self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') + + with self.assertRaises(TypeError): + script = script + None + self.assertEqual(script, b'\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01') + + def test_repr(self): + def T(script, expected_repr): + actual_repr = repr(script) + self.assertEqual(actual_repr, expected_repr) + + T( CScript([]), + 'CScript([])') + + T( CScript([1]), + 'CScript([1])') + + T( CScript([1, 2, 3]), + 'CScript([1, 2, 3])') + + T( CScript([1, x('7ac977d8373df875eceda362298e5d09d4b72b53'), OP_DROP]), + "CScript([1, x('7ac977d8373df875eceda362298e5d09d4b72b53'), OP_DROP])") + + T(CScript(x('0001ff515261ff')), + "CScript([x(''), x('ff'), 1, 2, OP_NOP, OP_INVALIDOPCODE])") + + # truncated scripts + T(CScript(x('6101')), + "CScript([OP_NOP, x('')...])") + + T(CScript(x('614bff')), + "CScript([OP_NOP, x('ff')...])") + + T(CScript(x('614c')), + "CScript([OP_NOP, ])") + + T(CScript(x('614c0200')), + "CScript([OP_NOP, x('00')...])") + + def test_is_p2sh(self): + def T(serialized, b): + script = CScript(x(serialized)) + self.assertEqual(script.is_p2sh(), b) + + # standard P2SH + T('a9146567e91196c49e1dffd09d5759f6bbc0c6d4c2e587', True) + + # NOT a P2SH txout due to the non-optimal PUSHDATA encoding + T('a94c146567e91196c49e1dffd09d5759f6bbc0c6d4c2e587', False) + + def test_is_push_only(self): + def T(serialized, b): + script = CScript(x(serialized)) + self.assertEqual(script.is_push_only(), b) + + T('', True) + T('00', True) + T('0101', True) + T('4c00', True) + T('4d0000', True) + T('4e00000000', True) + T('4f', True) + + # OP_RESERVED *is* considered to be a pushdata op by is_push_only! + # Or specifically, the IsPushOnly() used in P2SH validation. + T('50', True) + + T('51', True) + T('52', True) + T('53', True) + T('54', True) + T('55', True) + T('56', True) + T('57', True) + T('58', True) + T('59', True) + T('5a', True) + T('5b', True) + T('5c', True) + T('5d', True) + T('5e', True) + T('5f', True) + T('60', True) + + T('61', False) + + def test_is_push_only_on_invalid_pushdata(self): + def T(hex_script): + invalid_script = CScript(x(hex_script)) + self.assertFalse(invalid_script.is_push_only()) + + T('01') + T('02ff') + T('4b') + T('4c01') + T('4c02ff') + T('4d') + T('4d0100') + T('4d0200ff') + T('4e') + T('4e01000000') + T('4e02000000ff') + + def test_has_canonical_pushes(self): + def T(hex_script, expected_result): + script = CScript(x(hex_script)) + self.assertEqual(script.has_canonical_pushes(), expected_result) + + T('', True) + T('00', True) + T('FF', True) + + # could have used an OP_n code, rather than a 1-byte push + T('0100', False) + T('0101', False) + T('0102', False) + T('0103', False) + T('0104', False) + T('0105', False) + T('0106', False) + T('0107', False) + T('0108', False) + T('0109', False) + T('010A', False) + T('010B', False) + T('010C', False) + T('010D', False) + T('010E', False) + T('010F', False) + T('0110', False) + T('0111', True) + + # Could have used a normal n-byte push, rather than OP_PUSHDATA1 + T('4c00', False) + T('4c0100', False) + T('4c01FF', False) + T('4b' + '00'*75, True) + T('4c4b' + '00'*75, False) + T('4c4c' + '00'*76, True) + + # Could have used a OP_PUSHDATA1. + T('4d0000', False) + T('4d0100FF', False) + T('4dFF00' + 'FF'*0xFF, False) + T('4d0001' + 'FF'*0x100, True) + + # Could have used a OP_PUSHDATA2. + T('4e00000000', False) + T('4e01000000FF', False) + T('4eFFFF0000' + 'FF'*0xFFFF, False) + T('4e00000100' + 'FF'*0x10000, True) + + def test_has_canonical_pushes_with_invalid_truncated_script(self): + def T(hex_script): + script = CScript(x(hex_script)) + self.assertEqual(script.has_canonical_pushes(), False) + + T('01') + T('02ff') + T('4b') + T('4c01') + T('4c02ff') + T('4d') + T('4d0100') + T('4d0200ff') + T('4e') + T('4e01000000') + T('4e02000000ff') + + def test_is_unspendable(self): + def T(serialized, b): + script = CScript(x(serialized)) + self.assertEqual(script.is_unspendable(), b) + + T('', False) + T('00', False) + T('006a', False) + T('6a', True) + T('6a6a', True) + T('6a51', True) + + def test_is_valid(self): + def T(serialized, b): + script = CScript(x(serialized)) + self.assertEqual(script.is_valid(), b) + + T('', True) + T('00', True) + T('01', False) + + # invalid opcodes do not by themselves make a script invalid + T('ff', True) + + def test_to_p2sh_scriptPubKey(self): + def T(redeemScript, expected_hex_bytes): + redeemScript = CScript(redeemScript) + actual_script = redeemScript.to_p2sh_scriptPubKey() + self.assertEqual(b2x(actual_script), expected_hex_bytes) + + T([], + 'a914b472a266d0bd89c13706a4132ccfb16f7c3b9fcb87') + + T([1,x('029b6d2c97b8b7c718c325d7be3ac30f7c9d67651bce0c929f55ee77ce58efcf84'),1,OP_CHECKMULTISIG], + 'a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87') + + T([b'\xff'*517], + 'a9140da7fa40ebf248dfbca363c79921bdd665fed5ba87') + + with self.assertRaises(ValueError): + CScript([b'a' * 518]).to_p2sh_scriptPubKey() + +class Test_IsLowDERSignature(unittest.TestCase): + def test_high_s_value(self): + sig = x('3046022100820121109528efda8bb20ca28788639e5ba5b365e0a84f8bd85744321e7312c6022100a7c86a21446daa405306fe10d0a9906e37d1a2c6b6fdfaaf6700053058029bbe') + self.assertFalse(IsLowDERSignature(sig)) + def test_low_s_value(self): + sig = x('3045022100b135074e08cc93904a1712b2600d3cb01899a5b1cc7498caa4b8585bcf5f27e7022074ab544045285baef0a63f0fb4c95e577dcbf5c969c0bf47c7da8e478909d669') + self.assertTrue(IsLowDERSignature(sig)) diff --git a/bitcoin/tests/test_scripteval.py b/bitcoin/tests/test_scripteval.py new file mode 100644 index 0000000..4dbb994 --- /dev/null +++ b/bitcoin/tests/test_scripteval.py @@ -0,0 +1,82 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import json +import os +import unittest + +import sys +if sys.version > '3': + long = int + +from binascii import unhexlify + +from bitcoin.core import ValidationError +from bitcoin.core.script import * +from bitcoin.core.scripteval import * + +def parse_script(s): + def ishex(s): + return set(s).issubset(set('0123456789abcdefABCDEF')) + + r = [] + + # Create an opcodes_by_name table with both OP_ prefixed names and + # shortened ones with the OP_ dropped. + opcodes_by_name = {} + for name, code in OPCODES_BY_NAME.items(): + opcodes_by_name[name] = code + opcodes_by_name[name[3:]] = code + + for word in s.split(): + if word.isdigit() or (word[0] == '-' and word[1:].isdigit()): + r.append(CScript([long(word)])) + elif word.startswith('0x') and ishex(word[2:]): + # Raw ex data, inserted NOT pushed onto stack: + r.append(unhexlify(word[2:].encode('utf8'))) + elif len(word) >= 2 and word[0] == "'" and word[-1] == "'": + r.append(CScript([bytes(word[1:-1].encode('utf8'))])) + elif word in opcodes_by_name: + r.append(CScript([opcodes_by_name[word]])) + else: + raise ValueError("Error parsing script: %r" % s) + + return CScript(b''.join(r)) + + +def load_test_vectors(name): + with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: + for test_case in json.load(fd): + if len(test_case) < 3: + test_case.append('') + scriptSig, scriptPubKey, comment = test_case + + scriptSig = parse_script(scriptSig) + scriptPubKey = parse_script(scriptPubKey) + + yield (scriptSig, scriptPubKey, comment, test_case) + + +class Test_EvalScript(unittest.TestCase): + flags = (SCRIPT_VERIFY_P2SH, SCRIPT_VERIFY_STRICTENC) + def test_script_valid(self): + for scriptSig, scriptPubKey, comment, test_case in load_test_vectors('script_valid.json'): + try: + VerifyScript(scriptSig, scriptPubKey, None, 0, flags=self.flags) + except ValidationError as err: + self.fail('Script FAILED: %r %r %r with exception %r' % (scriptSig, scriptPubKey, comment, err)) + + def test_script_invalid(self): + for scriptSig, scriptPubKey, comment, test_case in load_test_vectors('script_invalid.json'): + with self.assertRaises(ValidationError): + VerifyScript(scriptSig, scriptPubKey, None, 0, flags=self.flags) diff --git a/bitcoin/tests/test_serialize.py b/bitcoin/tests/test_serialize.py new file mode 100644 index 0000000..7b21ba5 --- /dev/null +++ b/bitcoin/tests/test_serialize.py @@ -0,0 +1,136 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import unittest +import os + +from binascii import unhexlify + +from bitcoin.core.serialize import * + +class Test_Serializable(unittest.TestCase): + def test_extra_data(self): + """Serializable.deserialize() fails if extra data is present""" + + class FooSerializable(Serializable): + @classmethod + def stream_deserialize(cls, f): + return cls() + + def stream_serialize(self, f): + pass + + try: + FooSerializable.deserialize(b'\x00') + except DeserializationExtraDataError as err: + self.assertEqual(err.obj, FooSerializable()) + self.assertEqual(err.padding, b'\x00') + + else: + self.fail("DeserializationExtraDataError not raised") + + FooSerializable.deserialize(b'\x00', allow_padding=True) + +class Test_VarIntSerializer(unittest.TestCase): + def test(self): + def T(value, expected): + expected = unhexlify(expected) + actual = VarIntSerializer.serialize(value) + self.assertEqual(actual, expected) + roundtrip = VarIntSerializer.deserialize(actual) + self.assertEqual(value, roundtrip) + T(0x0, b'00') + T(0xfc, b'fc') + T(0xfd, b'fdfd00') + T(0xffff, b'fdffff') + T(0x10000, b'fe00000100') + T(0xffffffff, b'feffffffff') + T(0x100000000, b'ff0000000001000000') + T(0xffffffffffffffff, b'ffffffffffffffffff') + + def test_non_optimal(self): + def T(serialized, expected_value): + serialized = unhexlify(serialized) + actual_value = VarIntSerializer.deserialize(serialized) + self.assertEqual(actual_value, expected_value) + T(b'fd0000', 0) + T(b'fd3412', 0x1234) + T(b'fe00000000', 0) + T(b'fe67452301', 0x1234567) + T(b'ff0000000000000000', 0) + T(b'ffefcdab8967452301', 0x123456789abcdef) + + def test_truncated(self): + def T(serialized): + serialized = unhexlify(serialized) + with self.assertRaises(SerializationTruncationError): + VarIntSerializer.deserialize(serialized) + T(b'') + T(b'fd') + T(b'fd00') + T(b'fe') + T(b'fe00') + T(b'fe0000') + T(b'fe000000') + T(b'ff') + T(b'ff00000000000000') + +class Test_BytesSerializer(unittest.TestCase): + def test(self): + def T(value, expected): + value = unhexlify(value) + expected = unhexlify(expected) + actual = BytesSerializer.serialize(value) + self.assertEqual(actual, expected) + roundtrip = BytesSerializer.deserialize(actual) + self.assertEqual(value, roundtrip) + T(b'', b'00') + T(b'00', b'0100') + T(b'00'*0xffff, b'fdffff' + b'00'*0xffff) + + def test_truncated(self): + def T(serialized, ex_cls=SerializationTruncationError): + serialized = unhexlify(serialized) + with self.assertRaises(ex_cls): + BytesSerializer.deserialize(serialized) + T(b'') + T(b'01') + T(b'0200') + T(b'ff00000000000000ff11223344', SerializationError) # > max_size + +class Test_Compact(unittest.TestCase): + def test_from_compact_zero(self): + self.assertEqual(uint256_from_compact(0x00123456), 0) + self.assertEqual(uint256_from_compact(0x01003456), 0) + self.assertEqual(uint256_from_compact(0x02000056), 0) + self.assertEqual(uint256_from_compact(0x03000000), 0) + self.assertEqual(uint256_from_compact(0x04000000), 0) + self.assertEqual(uint256_from_compact(0x00923456), 0) + def test_from_compact_negative_zero(self): + # Negative bit isn't supported yet + # self.assertEqual(uint256_from_compact(0x01803456), 0) + # self.assertEqual(uint256_from_compact(0x02800056), 0) + # self.assertEqual(uint256_from_compact(0x03800000), 0) + # self.assertEqual(uint256_from_compact(0x04800000), 0) + return + + def test_twelve(self): + self.assertEqual(uint256_from_compact(0x01123456), 0x0012) + self.assertEqual(compact_from_uint256(0x0012), 0x01120000) + + def test_from_uint256(self): + self.assertEqual(compact_from_uint256(0x1234), 0x02123400) + self.assertEqual(compact_from_uint256(0x123456), 0x03123456) + self.assertEqual(compact_from_uint256(0x12345600), 0x04123456) + self.assertEqual(compact_from_uint256(0x92340000), 0x05009234) + self.assertEqual(compact_from_uint256(0x1234560000000000000000000000000000000000000000000000000000000000), 0x20123456) diff --git a/bitcoin/tests/test_transactions.py b/bitcoin/tests/test_transactions.py new file mode 100644 index 0000000..f96c5b3 --- /dev/null +++ b/bitcoin/tests/test_transactions.py @@ -0,0 +1,153 @@ +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import json +import unittest +import os + +from bitcoin.core import * +from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH + +from bitcoin.tests.test_scripteval import parse_script + +def load_test_vectors(name): + with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: + for test_case in json.load(fd): + # Comments designated by single length strings + if len(test_case) == 1: + continue + assert len(test_case) == 3 + + prevouts = {} + for json_prevout in test_case[0]: + assert len(json_prevout) == 3 + n = json_prevout[1] + if n == -1: + n = 0xffffffff + prevout = COutPoint(lx(json_prevout[0]), n) + prevouts[prevout] = parse_script(json_prevout[2]) + + tx = CTransaction.deserialize(x(test_case[1])) + enforceP2SH = test_case[2] + + yield (prevouts, tx, enforceP2SH) + +class Test_COutPoint(unittest.TestCase): + def test_is_null(self): + self.assertTrue(COutPoint().is_null()) + self.assertTrue(COutPoint(hash=b'\x00'*32,n=0xffffffff).is_null()) + self.assertFalse(COutPoint(hash=b'\x00'*31 + b'\x01').is_null()) + self.assertFalse(COutPoint(n=1).is_null()) + + def test_repr(self): + def T(outpoint, expected): + actual = repr(outpoint) + self.assertEqual(actual, expected) + T( COutPoint(), + 'COutPoint()') + T( COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0), + "COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0)") + + def test_str(self): + def T(outpoint, expected): + actual = str(outpoint) + self.assertEqual(actual, expected) + T(COutPoint(), + '0000000000000000000000000000000000000000000000000000000000000000:4294967295') + T(COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0), + '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0') + T(COutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 10), + '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:10') + +class Test_CMutableOutPoint(unittest.TestCase): + def test_GetHash(self): + """CMutableOutPoint.GetHash() is not cached""" + outpoint = CMutableOutPoint() + + h1 = outpoint.GetHash() + outpoint.n = 1 + + self.assertNotEqual(h1, outpoint.GetHash()) + + +class Test_CTxIn(unittest.TestCase): + def test_is_final(self): + self.assertTrue(CTxIn().is_final()) + self.assertTrue(CTxIn(nSequence=0xffffffff).is_final()) + self.assertFalse(CTxIn(nSequence=0).is_final()) + + def test_repr(self): + def T(txin, expected): + actual = repr(txin) + self.assertEqual(actual, expected) + T( CTxIn(), + 'CTxIn(COutPoint(), CScript([]), 0xffffffff)') + +class Test_CMutableTxIn(unittest.TestCase): + def test_GetHash(self): + """CMutableTxIn.GetHash() is not cached""" + txin = CMutableTxIn() + + h1 = txin.GetHash() + txin.prevout.n = 1 + + self.assertNotEqual(h1, txin.GetHash()) + +class Test_CTransaction(unittest.TestCase): + def test_is_coinbase(self): + tx = CMutableTransaction() + self.assertFalse(tx.is_coinbase()) + + tx.vin.append(CMutableTxIn()) + + # IsCoinBase() in reference client doesn't check if vout is empty + self.assertTrue(tx.is_coinbase()) + + tx.vin[0].prevout.n = 0 + self.assertFalse(tx.is_coinbase()) + + tx.vin[0] = CTxIn() + tx.vin.append(CTxIn()) + self.assertFalse(tx.is_coinbase()) + + def test_tx_valid(self): + for prevouts, tx, enforceP2SH in load_test_vectors('tx_valid.json'): + try: + CheckTransaction(tx) + except CheckTransactionError: + self.fail('tx failed CheckTransaction(): ' \ + + str((prevouts, b2x(tx.serialize()), enforceP2SH))) + continue + + for i in range(len(tx.vin)): + flags = set() + if enforceP2SH: + flags.add(SCRIPT_VERIFY_P2SH) + + VerifyScript(tx.vin[i].scriptSig, prevouts[tx.vin[i].prevout], tx, i, flags=flags) + + + def test_tx_invalid(self): + for prevouts, tx, enforceP2SH in load_test_vectors('tx_invalid.json'): + try: + CheckTransaction(tx) + except CheckTransactionError: + continue + + with self.assertRaises(ValidationError): + for i in range(len(tx.vin)): + flags = set() + if enforceP2SH: + flags.add(SCRIPT_VERIFY_P2SH) + + VerifyScript(tx.vin[i].scriptSig, prevouts[tx.vin[i].prevout], tx, i, flags=flags) diff --git a/bitcoin/tests/test_wallet.py b/bitcoin/tests/test_wallet.py new file mode 100644 index 0000000..812b56f --- /dev/null +++ b/bitcoin/tests/test_wallet.py @@ -0,0 +1,237 @@ +# Copyright (C) 2013-2015 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +from __future__ import absolute_import, division, print_function, unicode_literals + +import unittest + +from bitcoin.core import b2x, x +from bitcoin.core.script import CScript, IsLowDERSignature +from bitcoin.core.key import CPubKey +from bitcoin.wallet import * + +class Test_CBitcoinAddress(unittest.TestCase): + def test_create_from_string(self): + """Create CBitcoinAddress's from strings""" + + def T(str_addr, expected_bytes, expected_nVersion, expected_class): + addr = CBitcoinAddress(str_addr) + self.assertEqual(addr.to_bytes(), expected_bytes) + self.assertEqual(addr.nVersion, expected_nVersion) + self.assertEqual(addr.__class__, expected_class) + + T('1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', + x('62e907b15cbf27d5425399ebf6f0fb50ebb88f18'), 0, + P2PKHBitcoinAddress) + + T('37k7toV1Nv4DfmQbmZ8KuZDQCYK9x5KpzP', + x('4266fc6f2c2861d7fe229b279a79803afca7ba34'), 5, + P2SHBitcoinAddress) + + def test_wrong_nVersion(self): + """Creating a CBitcoinAddress from a unknown nVersion fails""" + + # tests run in mainnet, so both of the following should fail + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress('mpXwg4jMtRhuSpVq4xS3HFHmCmWp9NyGKt') + + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress('2MyJKxYR2zNZZsZ39SgkCXWCfQtXKhnWSWq') + + def test_from_scriptPubKey(self): + def T(hex_scriptpubkey, expected_str_address, expected_class): + scriptPubKey = CScript(x(hex_scriptpubkey)) + addr = CBitcoinAddress.from_scriptPubKey(scriptPubKey) + self.assertEqual(str(addr), expected_str_address) + self.assertEqual(addr.__class__, expected_class) + + T('a914000000000000000000000000000000000000000087', '31h1vYVSYuKP6AhS86fbRdMw9XHieotbST', + P2SHBitcoinAddress) + T('76a914000000000000000000000000000000000000000088ac', '1111111111111111111114oLvT2', + P2PKHBitcoinAddress) + + def test_from_nonstd_scriptPubKey(self): + """CBitcoinAddress.from_scriptPubKey() with non-standard scriptPubKeys""" + + # Bad P2SH scriptPubKeys + + # non-canonical pushdata + scriptPubKey = CScript(x('a94c14000000000000000000000000000000000000000087')) + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress.from_scriptPubKey(scriptPubKey) + + # Bad P2PKH scriptPubKeys + + # Missing a byte + scriptPubKey = CScript(x('76a914000000000000000000000000000000000000000088')) + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress.from_scriptPubKey(scriptPubKey) + + # One extra byte + scriptPubKey = CScript(x('76a914000000000000000000000000000000000000000088acac')) + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress.from_scriptPubKey(scriptPubKey) + + # One byte changed + scriptPubKey = CScript(x('76a914000000000000000000000000000000000000000088ad')) + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress.from_scriptPubKey(scriptPubKey) + + def test_from_invalid_scriptPubKey(self): + """CBitcoinAddress.from_scriptPubKey() with invalid scriptPubKeys""" + + # We should raise a CBitcoinAddressError, not any other type of error + + # Truncated P2SH + scriptPubKey = CScript(x('a91400000000000000000000000000000000000000')) + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress.from_scriptPubKey(scriptPubKey) + + # Truncated P2PKH + scriptPubKey = CScript(x('76a91400000000000000000000000000000000000000')) + with self.assertRaises(CBitcoinAddressError): + CBitcoinAddress.from_scriptPubKey(scriptPubKey) + + def test_to_scriptPubKey(self): + """CBitcoinAddress.to_scriptPubKey() works""" + def T(str_addr, expected_scriptPubKey_hexbytes): + addr = CBitcoinAddress(str_addr) + + actual_scriptPubKey = addr.to_scriptPubKey() + self.assertEqual(b2x(actual_scriptPubKey), expected_scriptPubKey_hexbytes) + + T('31h1vYVSYuKP6AhS86fbRdMw9XHieotbST', + 'a914000000000000000000000000000000000000000087') + + T('1111111111111111111114oLvT2', + '76a914000000000000000000000000000000000000000088ac') + +class Test_P2SHBitcoinAddress(unittest.TestCase): + def test_from_redeemScript(self): + addr = P2SHBitcoinAddress.from_redeemScript(CScript()) + self.assertEqual(str(addr), '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') + +class Test_P2PKHBitcoinAddress(unittest.TestCase): + def test_from_non_canonical_scriptPubKey(self): + def T(hex_scriptpubkey, expected_str_address): + scriptPubKey = CScript(x(hex_scriptpubkey)) + addr = P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) + self.assertEqual(str(addr), expected_str_address) + + # now test that CBitcoinAddressError is raised with accept_non_canonical_pushdata=False + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey, accept_non_canonical_pushdata=False) + + T('76a94c14000000000000000000000000000000000000000088ac', '1111111111111111111114oLvT2') + T('76a94d1400000000000000000000000000000000000000000088ac', '1111111111111111111114oLvT2'), + T('76a94e14000000000000000000000000000000000000000000000088ac', '1111111111111111111114oLvT2') + + # make sure invalid scripts raise CBitcoinAddressError + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_scriptPubKey(x('76a94c14')) + + def test_from_bare_checksig_scriptPubKey(self): + def T(hex_scriptpubkey, expected_str_address): + scriptPubKey = CScript(x(hex_scriptpubkey)) + addr = P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) + self.assertEqual(str(addr), expected_str_address) + + # now test that CBitcoinAddressError is raised with accept_non_canonical_pushdata=False + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey, accept_bare_checksig=False) + + # compressed + T('21000000000000000000000000000000000000000000000000000000000000000000ac', '14p5cGy5DZmtNMQwTQiytBvxMVuTmFMSyU') + + # uncompressed + T('410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ac', '1QLFaVVt99p1y18zWSZnespzhkFxjwBbdP') + + # non-canonical encoding + T('4c21000000000000000000000000000000000000000000000000000000000000000000ac', '14p5cGy5DZmtNMQwTQiytBvxMVuTmFMSyU') + + # odd-lengths are *not* accepted + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_scriptPubKey(x('2200000000000000000000000000000000000000000000000000000000000000000000ac')) + + def test_from_valid_pubkey(self): + """Create P2PKHBitcoinAddress's from valid pubkeys""" + + def T(pubkey, expected_str_addr): + addr = P2PKHBitcoinAddress.from_pubkey(pubkey) + self.assertEqual(str(addr), expected_str_addr) + + T(x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71'), + '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') + T(x('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455'), + '1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T') + + T(CPubKey(x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71')), + '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8') + T(CPubKey(x('0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455')), + '1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T') + + def test_from_invalid_pubkeys(self): + """Create P2PKHBitcoinAddress's from invalid pubkeys""" + + # first test with accept_invalid=True + def T(invalid_pubkey, expected_str_addr): + addr = P2PKHBitcoinAddress.from_pubkey(invalid_pubkey, accept_invalid=True) + self.assertEqual(str(addr), expected_str_addr) + + T(x(''), + '1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E') + T(x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c72'), + '1L9V4NXbNtZsLjrD3nkU7gtEYLWRBWXLiZ') + + # With accept_invalid=False we should get CBitcoinAddressError's + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_pubkey(x('')) + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_pubkey(x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c72')) + with self.assertRaises(CBitcoinAddressError): + P2PKHBitcoinAddress.from_pubkey(CPubKey(x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c72'))) + +class Test_CBitcoinSecret(unittest.TestCase): + def test(self): + def T(base58_privkey, expected_hex_pubkey, expected_is_compressed_value): + key = CBitcoinSecret(base58_privkey) + self.assertEqual(b2x(key.pub), expected_hex_pubkey) + self.assertEqual(key.is_compressed, expected_is_compressed_value) + + T('5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS', + '0478d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71a1518063243acd4dfe96b66e3f2ec8013c8e072cd09b3834a19f81f659cc3455', + False) + T('L3p8oAcQTtuokSCRHQ7i4MhjWc9zornvpJLfmg62sYpLRJF9woSu', + '0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71', + True) + + def test_sign(self): + key = CBitcoinSecret('5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS') + hash = b'\x00' * 32 + sig = key.sign(hash) + + # Check a valid signature + self.assertTrue(key.pub.verify(hash, sig)) + self.assertTrue(IsLowDERSignature(sig)) + + # Check invalid hash returns false + self.assertFalse(key.pub.verify(b'\xFF'*32, sig)) + # Check invalid signature returns false + self.assertFalse(key.pub.verify(hash, sig[0:-1] + b'\x00')) + + def test_sign_invalid_hash(self): + key = CBitcoinSecret('5KJvsngHeMpm884wtkJNzQGaCErckhHJBGFsvd3VyK5qMZXj3hS') + with self.assertRaises(TypeError): + sig = key.sign('0' * 32) + + hash = b'\x00' * 32 + with self.assertRaises(ValueError): + sig = key.sign(hash[0:-2]) diff --git a/bitcoin/wallet.py b/bitcoin/wallet.py new file mode 100644 index 0000000..9273259 --- /dev/null +++ b/bitcoin/wallet.py @@ -0,0 +1,255 @@ +# Copyright (C) 2012-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Wallet-related functionality + +Includes things like representing addresses and converting them to/from +scriptPubKeys; currently there is no actual wallet support implemented. +""" + +from __future__ import absolute_import, division, print_function, unicode_literals + +import sys +_bord = ord +if sys.version > '3': + _bord = lambda x: x + +import bitcoin +import bitcoin.base58 +import bitcoin.core +import bitcoin.core.key +import bitcoin.core.script as script + +class CBitcoinAddressError(bitcoin.base58.Base58Error): + """Raised when an invalid Bitcoin address is encountered""" + +class CBitcoinAddress(bitcoin.base58.CBase58Data): + """A Bitcoin address""" + + @classmethod + def from_bytes(cls, data, nVersion): + self = super(CBitcoinAddress, cls).from_bytes(data, nVersion) + + if nVersion == bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR']: + self.__class__ = P2SHBitcoinAddress + + elif nVersion == bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR']: + self.__class__ = P2PKHBitcoinAddress + + else: + raise CBitcoinAddressError('Version %d not a recognized Bitcoin Address' % nVersion) + + return self + + @classmethod + def from_scriptPubKey(cls, scriptPubKey): + """Convert a scriptPubKey to a CBitcoinAddress + + Returns a CBitcoinAddress subclass, either P2SHBitcoinAddress or + P2PKHBitcoinAddress. If the scriptPubKey is not recognized + CBitcoinAddressError will be raised. + """ + try: + return P2SHBitcoinAddress.from_scriptPubKey(scriptPubKey) + except CBitcoinAddressError: + pass + + try: + return P2PKHBitcoinAddress.from_scriptPubKey(scriptPubKey) + except CBitcoinAddressError: + pass + + raise CBitcoinAddressError('scriptPubKey not a valid address') + + def to_scriptPubKey(self): + """Convert an address to a scriptPubKey""" + raise NotImplementedError + +class P2SHBitcoinAddress(CBitcoinAddress): + @classmethod + def from_bytes(cls, data, nVersion=None): + if nVersion is None: + nVersion = bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR'] + + elif nVersion != bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR']: + raise ValueError('nVersion incorrect for P2SH address: got %d; expected %d' % \ + (nVersion, bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR'])) + + return super(P2SHBitcoinAddress, cls).from_bytes(data, nVersion) + + @classmethod + def from_redeemScript(cls, redeemScript): + """Convert a redeemScript to a P2SH address + + Convenience function: equivalent to P2SHBitcoinAddress.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey()) + """ + return cls.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey()) + + @classmethod + def from_scriptPubKey(cls, scriptPubKey): + """Convert a scriptPubKey to a P2SH address + + Raises CBitcoinAddressError if the scriptPubKey isn't of the correct + form. + """ + if scriptPubKey.is_p2sh(): + return cls.from_bytes(scriptPubKey[2:22], bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR']) + + else: + raise CBitcoinAddressError('not a P2SH scriptPubKey') + + def to_scriptPubKey(self): + """Convert an address to a scriptPubKey""" + assert self.nVersion == bitcoin.params.BASE58_PREFIXES['SCRIPT_ADDR'] + return script.CScript([script.OP_HASH160, self, script.OP_EQUAL]) + +class P2PKHBitcoinAddress(CBitcoinAddress): + @classmethod + def from_bytes(cls, data, nVersion=None): + if nVersion is None: + nVersion = bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR'] + + elif nVersion != bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR']: + raise ValueError('nVersion incorrect for P2PKH address: got %d; expected %d' % \ + (nVersion, bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR'])) + + return super(P2PKHBitcoinAddress, cls).from_bytes(data, nVersion) + + @classmethod + def from_pubkey(cls, pubkey, accept_invalid=False): + """Create a P2PKH bitcoin address from a pubkey + + Raises CBitcoinAddressError if pubkey is invalid, unless accept_invalid + is True. + + The pubkey must be a bytes instance; CECKey instances are not accepted. + """ + if not isinstance(pubkey, bytes): + raise TypeError('pubkey must be bytes instance; got %r' % pubkey.__class__) + + if not accept_invalid: + if not isinstance(pubkey, bitcoin.core.key.CPubKey): + pubkey = bitcoin.core.key.CPubKey(pubkey) + if not pubkey.is_fullyvalid: + raise CBitcoinAddressError('invalid pubkey') + + pubkey_hash = bitcoin.core.Hash160(pubkey) + return P2PKHBitcoinAddress.from_bytes(pubkey_hash) + + @classmethod + def from_scriptPubKey(cls, scriptPubKey, accept_non_canonical_pushdata=True, accept_bare_checksig=True): + """Convert a scriptPubKey to a P2PKH address + + Raises CBitcoinAddressError if the scriptPubKey isn't of the correct + form. + + accept_non_canonical_pushdata - Allow non-canonical pushes (default True) + accept_bare_checksig - Treat bare-checksig as P2PKH scriptPubKeys (default True) + """ + if accept_non_canonical_pushdata: + # Canonicalize script pushes + scriptPubKey = script.CScript(scriptPubKey) # in case it's not a CScript instance yet + + try: + scriptPubKey = script.CScript(tuple(scriptPubKey)) # canonicalize + except bitcoin.core.script.CScriptInvalidError: + raise CBitcoinAddressError('not a P2PKH scriptPubKey: script is invalid') + + if (len(scriptPubKey) == 25 + and _bord(scriptPubKey[0]) == script.OP_DUP + and _bord(scriptPubKey[1]) == script.OP_HASH160 + and _bord(scriptPubKey[2]) == 0x14 + and _bord(scriptPubKey[23]) == script.OP_EQUALVERIFY + and _bord(scriptPubKey[24]) == script.OP_CHECKSIG): + return cls.from_bytes(scriptPubKey[3:23], bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR']) + + elif accept_bare_checksig: + pubkey = None + + # We can operate on the raw bytes directly because we've + # canonicalized everything above. + if (len(scriptPubKey) == 35 # compressed + and _bord(scriptPubKey[0]) == 0x21 + and _bord(scriptPubKey[34]) == script.OP_CHECKSIG): + + pubkey = scriptPubKey[1:34] + + elif (len(scriptPubKey) == 67 # uncompressed + and _bord(scriptPubKey[0]) == 0x41 + and _bord(scriptPubKey[66]) == script.OP_CHECKSIG): + + pubkey = scriptPubKey[1:65] + + if pubkey is not None: + return cls.from_pubkey(pubkey, accept_invalid=True) + + raise CBitcoinAddressError('not a P2PKH scriptPubKey') + + def to_scriptPubKey(self): + """Convert an address to a scriptPubKey""" + assert self.nVersion == bitcoin.params.BASE58_PREFIXES['PUBKEY_ADDR'] + return script.CScript([script.OP_DUP, script.OP_HASH160, self, script.OP_EQUALVERIFY, script.OP_CHECKSIG]) + +class CKey(object): + """An encapsulated private key + + Attributes: + + pub - The corresponding CPubKey for this private key + is_compressed - True if compressed + + """ + def __init__(self, secret, compressed=True): + self._cec_key = bitcoin.core.key.CECKey() + self._cec_key.set_secretbytes(secret) + self._cec_key.set_compressed(compressed) + + self.pub = bitcoin.core.key.CPubKey(self._cec_key.get_pubkey(), self._cec_key) + + @property + def is_compressed(self): + return self.pub.is_compressed + + def sign(self, hash): + return self._cec_key.sign(hash) + + +class CBitcoinSecretError(bitcoin.base58.Base58Error): + pass + +class CBitcoinSecret(bitcoin.base58.CBase58Data, CKey): + """A base58-encoded secret key""" + + @classmethod + def from_secret_bytes(cls, secret, compressed=True): + """Create a secret key from a 32-byte secret""" + self = cls.from_bytes(secret + (b'\x01' if compressed else b''), + bitcoin.params.BASE58_PREFIXES['SECRET_KEY']) + self.__init__(None) + return self + + def __init__(self, s): + if self.nVersion != bitcoin.params.BASE58_PREFIXES['SECRET_KEY']: + raise CBitcoinSecretError('Not a base58-encoded secret key: got nVersion=%d; expected nVersion=%d' % \ + (self.nVersion, bitcoin.params.BASE58_PREFIXES['SECRET_KEY'])) + + CKey.__init__(self, self[0:32], len(self) > 32 and _bord(self[32]) == 1) + + +__all__ = ( + 'CBitcoinAddressError', + 'CBitcoinAddress', + 'P2SHBitcoinAddress', + 'P2PKHBitcoinAddress', + 'CKey', + 'CBitcoinSecretError', + 'CBitcoinSecret', +) diff --git a/examples/bip-0070-payment-protocol.py b/examples/bip-0070-payment-protocol.py new file mode 100755 index 0000000..6654429 --- /dev/null +++ b/examples/bip-0070-payment-protocol.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Bip-0070-related functionality + +Creates http response objects suitable for use with +bitcoin bip 70 using googles protocol buffers. +""" + +import urllib2 + +# https://github.com/bitcoin/bips/blob/master/bip-0070/paymentrequest.proto +import payments_pb2 +o = payments_pb2 + +import bitcoin +#bitcoin.SelectParams('testnet') +from bitcoin.wallet import CBitcoinAddress +from bitcoin.core.script import CScript +from bitcoin.rpc import Proxy + +from time import time + +def payment_request(): + """Generates a http PaymentRequest object""" + + bc = Proxy() + btc = bc.getnewaddress() + +# Setting the 'amount' field to 0 (zero) should prompt the user to enter +# the amount for us but a bug in bitcoin core qt version 0.9.1 (at time of +# writing) wrongly informs us that the value is too small and aborts. +# https://github.com/bitcoin/bitcoin/issues/3095 +# Also there can be no leading 0's (zeros). + btc_amount = 100000 + serialized_pubkey = btc.to_scriptPubKey() + + pdo = o.PaymentDetails() + #pdo.network = 'test' + pdo.outputs.add(amount = btc_amount,script = serialized_pubkey) + pdo.time = int(time()) + pdo.memo = 'String shown to user before confirming payment' + pdo.payment_url = 'http://payment_ack.url' + + pro = o.PaymentRequest() + pro.serialized_payment_details = pdo.SerializeToString() + + sds_pr = pro.SerializeToString() + + open('sds_pr_blob', 'wb').write(sds_pr) + headers = {'Content-Type': 'application/bitcoin-payment', + 'Accept': 'application/bitcoin-paymentrequest'} + http_response_object = urllib2.Request('file:sds_pr_blob', None, headers) + + return http_response_object + + +def payment_ack(serialized_Payment_message): + """Generates a PaymentACK object, captures client refund address and returns a message""" + + pao = o.PaymentACK() + pao.payment.ParseFromString(serialized_Payment_message) + pao.memo = 'String shown to user after payment confirmation' + + refund_address = CBitcoinAddress.from_scriptPubKey(CScript(pao.payment.refund_to[0].script)) + + sds_pa = pao.SerializeToString() + + open('sds_pa_blob', 'wb').write(sds_pa) + headers = {'Content-Type' : 'application/bitcoin-payment', 'Accept' : 'application/bitcoin-paymentack'} + http_response_object = urllib2.Request('file:sds_pa_blob', None, headers) + + return http_response_object diff --git a/examples/make-bootstrap-rpc.py b/examples/make-bootstrap-rpc.py new file mode 100755 index 0000000..349c93c --- /dev/null +++ b/examples/make-bootstrap-rpc.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2013-2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Make a boostrap.dat file by getting the blocks from the RPC interface.""" + +import sys +if sys.version_info.major < 3: + sys.stderr.write('Sorry, Python 3.x required by this example.\n') + sys.exit(1) + +import bitcoin +from bitcoin.core import CBlock +import bitcoin.rpc + +import struct +import sys +import time + +try: + if len(sys.argv) not in (2, 3): + raise Exception + + n = int(sys.argv[1]) + + if len(sys.argv) == 3: + bitcoin.SelectParams(sys.argv[2]) +except Exception as ex: + print('Usage: %s [network=(mainnet|testnet|regtest)] > bootstrap.dat' % sys.argv[0], file=sys.stderr) + sys.exit(1) + + +proxy = bitcoin.rpc.Proxy() + +total_bytes = 0 +start_time = time.time() + +fd = sys.stdout.buffer +for i in range(n + 1): + block = proxy.getblock(proxy.getblockhash(i)) + + block_bytes = block.serialize() + + total_bytes += len(block_bytes) + print('%.2f KB/s, height %d, %d bytes' % + ((total_bytes / 1000) / (time.time() - start_time), + i, len(block_bytes)), + file=sys.stderr) + + fd.write(bitcoin.params.MESSAGE_START) + fd.write(struct.pack('= 1: + logging.root.setLevel(logging.DEBUG) +elif args.verbosity == -1: + logging.root.setLevel(logging.WARNING) +elif args.verbosity <= -2: + logging.root.setLevel(logging.ERROR) + +if args.testnet: + bitcoin.SelectParams('testnet') +elif args.regtest: + bitcoin.SelectParams('regtest') + +proxy = bitcoin.rpc.Proxy() + +if args.privkey is None: + args.privkey = CBitcoinSecret.from_secret_bytes(os.urandom(32)) + +else: + args.privkey = CBitcoinSecret(args.privkey) + +logging.info('Using keypair %s %s' % (b2x(args.privkey.pub), args.privkey)) + +# Turn the text file into padded lines +if args.fd is sys.stdin: + # work around a bug where even though we specified binary encoding we get + # the sys.stdin instead. + args.fd = sys.stdin.buffer +raw_padded_lines = [b'\x00' + line.rstrip().ljust(args.min_len) + b'\x00' for line in args.fd.readlines()] + +# combine lines if < MAX_SCRIPT_ELEMENT_SIZE +padded_lines = [] +prev_line = b'\x00' +for line in raw_padded_lines: + if len(prev_line) + len(line) <= MAX_SCRIPT_ELEMENT_SIZE: + prev_line = prev_line + line[1:] + + else: + padded_lines.append(prev_line) + prev_line = line + +if prev_line: + padded_lines.append(prev_line) + +scripts = [] +while padded_lines: + def make_scripts(lines, n): + # The n makes sure every p2sh addr is unique; the pubkey lets us + # control the order the vin order vs. just using hashlocks. + redeemScript = [] + for chunk in reversed(lines): + if len(chunk) > MAX_SCRIPT_ELEMENT_SIZE: + parser.exit('Lines must be less than %d characters; got %d characters' %\ + (MAX_SCRIPT_ELEMENT_SIZE, len(chunk))) + redeemScript.extend([OP_HASH160, Hash160(chunk), OP_EQUALVERIFY]) + redeemScript = CScript(redeemScript + + [args.privkey.pub, OP_CHECKSIGVERIFY, + n, OP_DROP, # deduplicate push dropped to meet BIP62 rules + OP_DEPTH, 0, OP_EQUAL]) # prevent scriptSig malleability + + return CScript(lines) + redeemScript, redeemScript + + scriptSig = redeemScript = None + for i in range(len(padded_lines)): + next_scriptSig, next_redeemScript = make_scripts(padded_lines[0:i+1], len(scripts)) + + # FIXME: magic numbers! + if len(next_redeemScript) > 520 or len(next_scriptSig) > 1600-100: + padded_lines = padded_lines[i:] + break + + else: + scriptSig = next_scriptSig + redeemScript = next_redeemScript + + else: + padded_lines = [] + + scripts.append((scriptSig, redeemScript)) + +# pay to the redeemScripts to make them spendable + +# the 41 accounts for the size of the CTxIn itself +payments = {P2SHBitcoinAddress.from_redeemScript(redeemScript):int(((len(scriptSig)+41)/1000 * args.fee_per_kb)*COIN) + for scriptSig, redeemScript in scripts} + +prevouts_by_scriptPubKey = None +if not args.dryrun: + txid = proxy.sendmany('', payments, 0) + + logging.info('Sent pre-pub tx: %s' % b2lx(txid)) + + tx = proxy.getrawtransaction(txid) + + prevouts_by_scriptPubKey = {txout.scriptPubKey:COutPoint(txid, i) for i, txout in enumerate(tx.vout)} + +else: + prevouts_by_scriptPubKey = {redeemScript.to_p2sh_scriptPubKey():COutPoint(b'\x00'*32, i) + for i, (scriptSig, redeemScript) in enumerate(scripts)} + logging.debug('Payments: %r' % payments) + logging.info('Total cost: %s BTC' % str_money_value(sum(amount for addr, amount in payments.items()))) + +# Create unsigned tx for SignatureHash + +# By paying this rather than an OP_RETURN the tx shows up on bc.i, convenient +# for determining propagation; inception for the lulz. +# +# FIXME: these 600 satoshi's aren't taken into account above... +vout = [CTxOut(600, CScript().to_p2sh_scriptPubKey().to_p2sh_scriptPubKey())] +#vout = [CTxOut(0, CScript([OP_RETURN]))] + +unsigned_vin = [] +for scriptSig, redeemScript in scripts: + scriptPubKey = redeemScript.to_p2sh_scriptPubKey() + + txin = CTxIn(prevouts_by_scriptPubKey[scriptPubKey]) + unsigned_vin.append(txin) +unsigned_tx = CTransaction(unsigned_vin, vout) + +# Sign! +signed_vin = [] +for i, (scriptSig, redeemScript) in enumerate(scripts): + sighash = SignatureHash(redeemScript, unsigned_tx, i, SIGHASH_NONE) + sig = args.privkey.sign(sighash) + bytes([SIGHASH_NONE]) + + signed_scriptSig = CScript([sig] + list(scriptSig)) + + txin = CTxIn(unsigned_vin[i].prevout, signed_scriptSig) + + signed_vin.append(txin) + +signed_tx = CTransaction(signed_vin, vout) + +if args.dryrun: + serialized_tx = signed_tx.serialize() + logging.info('tx size: %d bytes' % len(serialized_tx)) + logging.debug('hex: %s' % b2x(serialized_tx)) + +else: + # FIXME: the tx could be too long here, but there's no way to get sendmany + # to *not* broadcast the transaction first. This is a proof-of-concept, so + # punting. + + logging.debug('Sending publish tx, hex: %s' % b2x(signed_tx.serialize())) + txid = proxy.sendrawtransaction(signed_tx) + logging.info('Sent publish tx: %s' % b2lx(txid)) + diff --git a/examples/send-addrs-msg.py b/examples/send-addrs-msg.py new file mode 100644 index 0000000..9346fa7 --- /dev/null +++ b/examples/send-addrs-msg.py @@ -0,0 +1,64 @@ +import socket, time, bitcoin +from bitcoin.messages import msg_version, msg_verack, msg_addr +from bitcoin.net import CAddress + + +PORT = 18333 + +bitcoin.SelectParams('testnet') + +def version_pkt(client_ip, server_ip): + msg = msg_version() + msg.nVersion = 70002 + msg.addrTo.ip = server_ip + msg.addrTo.port = PORT + msg.addrFrom.ip = client_ip + msg.addrFrom.port = PORT + + return msg + +def addr_pkt( str_addrs ): + msg = msg_addr() + addrs = [] + for i in str_addrs: + addr = CAddress() + addr.port = 18333 + addr.nTime = int(time.time()) + addr.ip = i + + addrs.append( addr ) + msg.addrs = addrs + return msg + +s = socket.socket() + +server_ip = "192.168.0.149" +client_ip = "192.168.0.13" + +s.connect( (server_ip,PORT) ) + +# Send Version packet +s.send( version_pkt(client_ip, server_ip).to_bytes() ) + +# Get Version reply +print s.recv(1924) + +# Send Verack +s.send( msg_verack().to_bytes() ) +# Get Verack +print s.recv(1024) + +# Send Addrs +s.send( addr_pkt(["252.11.1.2", "EEEE:7777:8888:AAAA::1"]).to_bytes() ) + +time.sleep(1) +s.close() + +# debug log on the server should look like: +# accepted connection 192.168.0.13:39979 +# send version message: version 70002, blocks=317947, us=****, them=0.0.0.0:0, peer=192.168.0.13:39979 +# receive version message: /pythonbitcoin0.0.1/: version 70002, blocks=-1, us=192.168.0.149:18333, them=192.168.0.13:18333, peer=192.168.0.13:39979 +# Added 2 addresses from 192.168.0.13: 3 tried, 1706 new +# disconnecting node 192.168.0.13:39979 + + diff --git a/examples/spend-p2pkh-txout.py b/examples/spend-p2pkh-txout.py new file mode 100755 index 0000000..7da294a --- /dev/null +++ b/examples/spend-p2pkh-txout.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Low-level example of how to spend a standard pay-to-pubkey-hash (P2PKH) txout""" + +import sys +if sys.version_info.major < 3: + sys.stderr.write('Sorry, Python 3.x required by this example.\n') + sys.exit(1) + +import hashlib + +from bitcoin import SelectParams +from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160 +from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL +from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH +from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret + +SelectParams('mainnet') + +# Create the (in)famous correct brainwallet secret key. +h = hashlib.sha256(b'correct horse battery staple').digest() +seckey = CBitcoinSecret.from_secret_bytes(h) + +# Same as the txid:vout the createrawtransaction RPC call requires +# +# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin +# transaction hashes are shown little-endian rather than the usual big-endian. +# There's also a corresponding x() convenience function that takes big-endian +# hex and converts it to bytes. +txid = lx('7e195aa3de827814f172c362fcf838d92ba10e3f9fdd9c3ecaf79522b311b22d') +vout = 0 + +# Create the txin structure, which includes the outpoint. The scriptSig +# defaults to being empty. +txin = CMutableTxIn(COutPoint(txid, vout)) + +# We also need the scriptPubKey of the output we're spending because +# SignatureHash() replaces the transaction scriptSig's with it. +# +# Here we'll create that scriptPubKey from scratch using the pubkey that +# corresponds to the secret key we generated above. +txin_scriptPubKey = CScript([OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]) + +# Create the txout. This time we create the scriptPubKey from a Bitcoin +# address. +txout = CMutableTxOut(0.001*COIN, CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey()) + +# Create the unsigned transaction. +tx = CMutableTransaction([txin], [txout]) + +# Calculate the signature hash for that transaction. +sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL) + +# Now sign it. We have to append the type of signature we want to the end, in +# this case the usual SIGHASH_ALL. +sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) + +# Set the scriptSig of our transaction input appropriately. +txin.scriptSig = CScript([sig, seckey.pub]) + +# Verify the signature worked. This calls EvalScript() and actually executes +# the opcodes in the scripts to see if everything worked out. If it doesn't an +# exception will be raised. +VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) + +# Done! Print the transaction to standard output with the bytes-to-hex +# function. +print(b2x(tx.serialize())) diff --git a/examples/spend-p2sh-txout.py b/examples/spend-p2sh-txout.py new file mode 100755 index 0000000..9f0215b --- /dev/null +++ b/examples/spend-p2sh-txout.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Low-level example of how to spend a P2SH/BIP16 txout""" + +import sys +if sys.version_info.major < 3: + sys.stderr.write('Sorry, Python 3.x required by this example.\n') + sys.exit(1) + +import hashlib + +from bitcoin import SelectParams +from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160 +from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL +from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH +from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret + +SelectParams('mainnet') + +# Create the (in)famous correct brainwallet secret key. +h = hashlib.sha256(b'correct horse battery staple').digest() +seckey = CBitcoinSecret.from_secret_bytes(h) + +# Create a redeemScript. Similar to a scriptPubKey the redeemScript must be +# satisfied for the funds to be spent. +txin_redeemScript = CScript([seckey.pub, OP_CHECKSIG]) +print(b2x(txin_redeemScript)) + +# Create the magic P2SH scriptPubKey format from that redeemScript. You should +# look at the CScript.to_p2sh_scriptPubKey() function in bitcoin.core.script to +# understand what's happening, as well as read BIP16: +# https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki +txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() + +# Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it. +# You'll need to send some funds to it to create a txout to spend. +txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) +print('Pay to:',str(txin_p2sh_address)) + +# Same as the txid:vout the createrawtransaction RPC call requires +# +# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin +# transaction hashes are shown little-endian rather than the usual big-endian. +# There's also a corresponding x() convenience function that takes big-endian +# hex and converts it to bytes. +txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae') +vout = 0 + +# Create the txin structure, which includes the outpoint. The scriptSig +# defaults to being empty. +txin = CMutableTxIn(COutPoint(txid, vout)) + +# Create the txout. This time we create the scriptPubKey from a Bitcoin +# address. +txout = CMutableTxOut(0.0005*COIN, CBitcoinAddress('323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp').to_scriptPubKey()) + +# Create the unsigned transaction. +tx = CMutableTransaction([txin], [txout]) + +# Calculate the signature hash for that transaction. Note how the script we use +# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG +# operation happens EvalScript() will be evaluating the redeemScript, so the +# corresponding SignatureHash() function will use that same script when it +# replaces the scriptSig in the transaction being hashed with the script being +# executed. +sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL) + +# Now sign it. We have to append the type of signature we want to the end, in +# this case the usual SIGHASH_ALL. +sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) + +# Set the scriptSig of our transaction input appropriately. +txin.scriptSig = CScript([sig, txin_redeemScript]) + +# Verify the signature worked. This calls EvalScript() and actually executes +# the opcodes in the scripts to see if everything worked out. If it doesn't an +# exception will be raised. +VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) + +# Done! Print the transaction to standard output with the bytes-to-hex +# function. +print(b2x(tx.serialize())) diff --git a/examples/timestamp-op-ret.py b/examples/timestamp-op-ret.py new file mode 100755 index 0000000..63d4758 --- /dev/null +++ b/examples/timestamp-op-ret.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2014 The python-bitcoinlib developers +# +# This file is part of python-bitcoinlib. +# +# It is subject to the license terms in the LICENSE file found in the top-level +# directory of this distribution. +# +# No part of python-bitcoinlib, including this file, may be copied, modified, +# propagated, or distributed except according to the terms contained in the +# LICENSE file. + +"""Example of timestamping a file via OP_RETURN""" + +import sys +if sys.version_info.major < 3: + sys.stderr.write('Sorry, Python 3.x required by this example.\n') + sys.exit(1) + +import hashlib +import bitcoin.rpc +import sys + +from bitcoin import params +from bitcoin.core import * +from bitcoin.core.script import * + +proxy = bitcoin.rpc.Proxy() + +assert len(sys.argv) > 1 + +digests = [] +for f in sys.argv[1:]: + try: + with open(f, 'rb') as fd: + digests.append(Hash(fd.read())) + except FileNotFoundError as exp: + if len(f)/2 in (20, 32): + digests.append(x(f)) + else: + raise exp + except IOError as exp: + print(exp, file=sys.stderr) + continue + +for digest in digests: + txouts = [] + + unspent = sorted(proxy.listunspent(0), key=lambda x: hash(x['amount'])) + + txins = [CTxIn(unspent[-1]['outpoint'])] + value_in = unspent[-1]['amount'] + + change_addr = proxy.getnewaddress() + change_pubkey = proxy.validateaddress(change_addr)['pubkey'] + change_out = CMutableTxOut(params.MAX_MONEY, CScript([change_pubkey, OP_CHECKSIG])) + + digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, digest]))] + + txouts = [change_out] + digest_outs + + tx = CMutableTransaction(txins, txouts) + + + FEE_PER_BYTE = 0.00025*COIN/1000 + while True: + tx.vout[0].nValue = int(value_in - max(len(tx.serialize()) * FEE_PER_BYTE, 0.00011*COIN)) + + r = proxy.signrawtransaction(tx) + assert r['complete'] + tx = r['tx'] + + if value_in - tx.vout[0].nValue >= len(tx.serialize()) * FEE_PER_BYTE: + print(b2x(tx.serialize())) + print(len(tx.serialize()), 'bytes', file=sys.stderr) + print(b2lx(proxy.sendrawtransaction(tx))) + break diff --git a/release-notes.md b/release-notes.md new file mode 100644 index 0000000..0b4d675 --- /dev/null +++ b/release-notes.md @@ -0,0 +1,78 @@ +python-bitcoinlib release notes +=============================== + +v0.4.0 +====== + +Major fix: OpenSSL 1.0.1k rejects non-canonical DER signatures, which Bitcoin +Core does not, so we now canonicalize signatures prior to passing them to +OpenSSL. Secondly we now only generate low-S DER signatures as per BIP62. + +API changes that might break compatibility with existing code: + +* MAX_MONEY is now a core chain parameter +* MainParams now inherits from CoreMainParams rather than CoreChainParams +* str() now returns hash:n format; previously was same as repr() +* RawProxy() no longer has _connection parameter + +Notable bugfixes: + +* MsgSerializable.to_bytes() no longer clobbers testnet params +* HTTPS RPC connections now use port 443 as default +* No longer assumes bitcoin.conf specifes rpcuser + +New features: + +* New RPC calls: dumpprivkey, importaddress +* Added P2P support for msg_notfound and msg_reject +* Added support for IPv6 addr messages + + +v0.3.0 +====== + +Major change: cleaned up what symbols are exported by modules. \_\_all\_\_ is now +used extensively, which may break some applications that were not importing the +right modules. Along those lines some implementation details like the ssl +attribute of the bitcoin.core.key module, and the entire bitcoin.core.bignum +module, are no longer part of the public API. This should not affect too many +users, but it will break some code. + +Other notable changes: + +* New getreceivedbyaddress RPC call. +* Fixed getbalance RPC call when wallet is configured off. +* Various code cleanups and minor bug fixes. + + +v0.2.1 +====== + +* Improve bitcoin address handling. P2SH and P2PKH addresses now get their own + classes - P2SHBitcoinAddress and P2PKHBitcoinAddress respectively - and P2PKH + can now convert scriptPubKeys containing non-canonical pushes as well as bare + checksig to addresses. +* .deserialize() methods now fail if there is extra data left over. +* Various other small bugfixes. +* License is now LGPL v3 or later. + + +v0.2.0 +====== + +Major change: CTransaction, CBlock, etc. now come in immutable (default) and +mutable forms. In most cases mutable and immutable can be used interchangeably; +when that is not possible methods are provided to create new (im)mutable +objects from (im)mutable ones efficiently. + +Other changes: + +* New BIP70 payment protocol example. (Derren Desouza) +* Rework of message serialization. Note that this may not represent the final + form of P2P support, which is still in flux. (Florian Schmaus) +* Various bugfixes + +Finally starting this release, git tags will be of the form +'python-bitcoinlib-(version)', replacing the less specific '(version)' form +previously used. + diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..4bda279 --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +from setuptools import setup, find_packages +import os + +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README')) as f: + README = f.read() + +requires = [] + +setup(name='python-bitcoinlib', + version='0.4.1-SNAPSHOT', + description='The Swiss Army Knife of the Bitcoin protocol.', + long_description=README, + classifiers=[ + "Programming Language :: Python", + ], + url='https://github.com/petertodd/python-bitcoinlib', + keywords='bitcoin', + packages=find_packages(), + zip_safe=False, + install_requires=requires, + test_suite="bitcoin.tests" + )