├── .gitignore ├── .travis.yml ├── LICENCE.txt ├── README.md ├── TODO.md ├── appveyor.yml ├── bitforge ├── __init__.py ├── address.py ├── compat.py ├── encoding.py ├── errors.py ├── hdprivkey.py ├── hdpubkey.py ├── networks.py ├── privkey.py ├── pubkey.py ├── script │ ├── __init__.py │ ├── instruction.py │ ├── interpreter.py │ ├── opcode.py │ └── script.py ├── signature.py ├── tools.py ├── transaction │ ├── __init__.py │ ├── input.py │ ├── output.py │ └── transaction.py ├── unit.py ├── uri.py └── utils │ ├── __init__.py │ ├── ecdsa.py │ ├── ellipticcurve.py │ ├── encoding.py │ ├── intbytes.py │ ├── numbertheory.py │ └── secp256k1.py ├── examples ├── example_pubkey.py ├── example_serial.py └── example_utils.py ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── data ├── invalid_wifs.json ├── privkey.json ├── tx_invalid.json ├── tx_valid.json └── valid_wifs.json ├── script ├── test_interpreter.py ├── test_opcode.py └── test_script.py ├── test_address.py ├── test_privkey.py ├── test_pubkey.py ├── test_transaction.py ├── test_unit.py └── test_uri.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *~ 4 | dist 5 | build 6 | *egg-info 7 | .DS_STORE 8 | .tox 9 | env/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.3" 5 | - "3.4" 6 | - "3.5" 7 | # command to install dependencies 8 | install: "python setup.py install" 9 | # command to run tests 10 | script: "python setup.py test" 11 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 by Yemel Jardi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitforge 2 | 3 | [![Build Status](https://travis-ci.org/coinforge/bitforge.svg?branch=master&style=flat-square)](https://travis-ci.org/coinforge/bitforge) 4 | [![Windows Build status](https://ci.appveyor.com/api/projects/status/kgbrc081lib3yh4f?svg=true)](https://ci.appveyor.com/project/federicobond/bitforge) 5 | [![Code Health](https://landscape.io/github/coinforge/bitforge/master/landscape.svg?style=flat)](https://landscape.io/github/coinforge/bitforge/master) 6 | 7 | The next great Bitcoin library, written in pure Python, **currently in beta and under 8 | development**. 9 | 10 | `Bitforge` provides a solid model of Bitcoin objects, through method-rich immutable 11 | instances and precise error descriptions. 12 | 13 | Currently, `Bitforge` supports: 14 | 15 | - Key creation and handling, with `PrivateKey` and `PublicKey` 16 | - Key derivation, with `HDPrivateKey` and `HDPublicKey` 17 | - Script compilation and evaluation, through `Script` and `Interpreter` 18 | - Pay-to-Pubkey and Pay-to-Script, with `Input`, `Output`, and their subclasses 19 | - Transaction creation and signing, through `Transaction` 20 | 21 | This is enough to create, sign and serialize a multisig `Transaction`. The 22 | process takes 5-10 lines of code. 23 | 24 | `Bitforge` will eventually provide: 25 | 26 | - A high-level _fluent_ interface, with progressive builders to wrap the immutable 27 | layer below. 28 | - Full test coverage (currently partial) 29 | 30 | 31 | # Examples 32 | 33 | ##### Send bitcoins from an address to another 34 | 35 | ```python 36 | from bitforge import PrivateKey, AddressInput, AddressOutput, Transaction 37 | 38 | # 1. Create an AddressInput with details from the Unspent Transaction Output 39 | in0 = AddressInput.create( 40 | tx_id = '4baa7551933fbf26158a619c3084ccdd5c0d81930b3e74a85a33ad26d13f1a55', 41 | txo_index = 0, 42 | address = Address.from_string('1Dy6qCRsjJ4Y3BYv7m9nf12aUMXD4RWMHC') 43 | ) 44 | 45 | # 2. Create an AddressOutput that you can redeem with your PrivateKey: 46 | privkey = PrivateKey.from_hex('21c601c0ae6dfcdcf622e6fe2be9153ed7ada0cc90a8a08475e57060e18c0791') 47 | 48 | out0 = AddressOutput.create( 49 | amount = 1000, # satoshis 50 | address = privkey.to_address() 51 | ) 52 | 53 | # 3. Create the Transaction: 54 | tx = Transaction(inputs = [ in0 ], outputs = [ out0 ]) 55 | 56 | # 4. Sign the first Input: 57 | signed_tx = tx.sign([ privkey ], 0) 58 | ``` 59 | 60 | 61 | ##### Send funds to a multisig address 62 | 63 | ```python 64 | from bitforge import PrivateKey, AddressInput, MultisigOutput, Transaction 65 | 66 | # 1. Create an AddressInput with details from the Unspent Transaction Output 67 | in0 = AddressInput.create( 68 | tx_id = '4baa7551933fbf26158a619c3084ccdd5c0d81930b3e74a85a33ad26d13f1a55', 69 | txo_index = 0, 70 | address = Address.from_string('1Dy6qCRsjJ4Y3BYv7m9nf12aUMXD4RWMHC') 71 | ) 72 | 73 | # 2. Create a MultisigOutput, given a known public keys and required signatures: 74 | out0 = MultisigOutput.create( 75 | amount = 1000, 76 | pubkeys = [ pubkey1, pubkey2, pubkey3 ], 77 | min_signatures = 2 78 | ) 79 | 80 | # 3. Create the Transaction: 81 | tx = Transaction(inputs = [ in0 ], outputs = [ out0 ]) 82 | 83 | # 4. Sign the first Input: 84 | signed_tx = tx.sign([ privkey ], 0) 85 | ``` 86 | 87 | 88 | # Object Model 89 | 90 | All of the classes described below extend `namedtuple`. Once created, their 91 | basic properties are **immutable**. They can be hashed, compared, printed and 92 | serialized. 93 | 94 | These basic classes are available for `import` in the `bitforge` module. 95 | 96 | ```python 97 | from bitforge import PrivateKey, Transaction 98 | ``` 99 | 100 | ## PrivateKey 101 | 102 | ##### `PrivateKey(secret = None, network = networks.default, compressed = True)` 103 | 104 | A `PrivateKey` object holds a `secret` number, or generates a random `secret` 105 | if `None` is provided. The `secret` must be an `int` between 1 and SECP256k1 106 | maximum. 107 | 108 | It's usually created using one of the factory methods listed below. 109 | 110 | 111 | #### Static methods 112 | 113 | ##### `PrivateKey.from_bytes(bytes, network = networks.default, compressed = True)` 114 | Create a new `PrivateKey` from a 32-byte binary `str` holding the `secret`. 115 | 116 | ##### `PrivateKey.from_hex(string, network = networks.default, compressed = True)` 117 | Create a new `PrivateKey` from a 64-byte hexadecimal `str` holding the `secret`. 118 | 119 | ##### `PrivateKey.from_wif(string)` 120 | Create a new `PrivateKey` from a WIF-encoded `str`. It already includes `network` 121 | and `compressed`. 122 | 123 | 124 | #### Instance methods 125 | 126 | ##### `.to_bytes()` 127 | Returns the `secret` as a 32-byte `str`. 128 | 129 | ##### `.to_hex()` 130 | Returns the `secret` as a 64-byte hexadecimal `str`. 131 | 132 | ##### `.to_wif()` 133 | Returns a WIF-encoded key, including details from `network` and `compressed`. 134 | 135 | ##### `.to_public_key()` 136 | Returns a matching `PublicKey` instance. 137 | 138 | ##### `.to_address()` 139 | Same as `to_public_key().to_address()`. 140 | 141 | ##### `.sign(payload)` 142 | Returns a binary `str` containing the signed `payload` in Bitcoin-compatible DER 143 | format, using the SECPK256k1 elliptic curve. 144 | 145 | ##### `.verify(signature, payload)` 146 | Verify that `signature` is valid for `payload`. 147 | 148 | 149 | 150 | ## PublicKey 151 | 152 | ##### `PublicKey(pair, network = networks.default, compressed = True)` 153 | 154 | A `PublicKey` object holds a `pair` of coordinates in the Bitcoin elliptic 155 | curve. 156 | 157 | It's usually extracted from a `PrivateKey`, or created using one of the factory 158 | methods listed below. 159 | 160 | 161 | #### Static methods 162 | 163 | ##### `PublicKey.from_bytes(bytes, network = networks.default)` 164 | Create a new `PublicKey` from a binary `str` holding a `pair`, auto-detecting 165 | if it's `compressed`. 166 | 167 | ##### `PublicKey.from_hex(string, network = networks.default)` 168 | Create a new `PublicKey` from a hexadecimal `pair`, auto-detecting if it's 169 | `compressed`. 170 | 171 | 172 | #### Instance methods 173 | 174 | ##### `.to_bytes()` 175 | Return a binary `str` representing the `pair`, which may be `compressed`. 176 | 177 | ##### `.to_hex()` 178 | Return a hexadecimal `str` representing the `pair`, which may be `compressed` 179 | 180 | ##### `.to_address()` 181 | Return a matching `Address` instance. 182 | 183 | 184 | 185 | ## Address 186 | 187 | ##### `Address(phash, network = networks.default, type = Address.Type.PublicKey)` 188 | 189 | An `Address` holds a Bitcoin address, hashed from a `PublicKey`. 190 | 191 | It's usually extracted from a `PublicKey`, or created using one of the factory 192 | methods listed below. 193 | 194 | 195 | #### Static methods 196 | 197 | ##### `Address.from_string(string)` 198 | Create a new `Address` from a base58check-encoded `str`, auto-detecting `network` 199 | and `type`. 200 | 201 | ##### `Address.from_bytes(bytes)` 202 | Create a new `Address` from a binary `str`, auto-detecting `network` and `type`. 203 | 204 | ##### `Address.from_hex(string, network = networks.default)` 205 | Create a new `Address` from a hexadecimal `str`, auto-detecting `network` and 206 | `type`. 207 | 208 | ##### `Address.from_public_key(pubkey)` 209 | Create a new `Address`, derived from a `PublicKey`. Same as `pubkey.to_address()`. 210 | 211 | ##### `Address.from_script(script, network = networks.default)` 212 | Create a new `Address` from a `Script`, for Pay-to-Script transactions. 213 | 214 | 215 | #### Instance methods 216 | 217 | ##### `.to_string()` 218 | Return a base58check-encoded `str` representing the `Address`, including the 219 | network and type prefix. 220 | 221 | ##### `.to_bytes()` 222 | Return a binary `str` representing the `Address`, including the network and type 223 | prefix. 224 | 225 | ##### `.to_hex()` 226 | Return a hexadecimal `str` representing the `Address`, including the network and 227 | type prefix. 228 | 229 | 230 | 231 | ## Input 232 | 233 | ##### `Input(tx_id, txo_index, script, seq_number = FINAL_SEQ_NUMBER)` 234 | 235 | A `Transaction` `Input`. `tx_id` and `txo_index` point to an unspent transaction 236 | output. 237 | 238 | The `Input` class can be instantiated directly, but the `sign()` method will 239 | `raise`. To really work with `Inputs`, you should use or create a subclass. 240 | These are available out-of-the-box, and described below: 241 | 242 | ``` 243 | Input 244 | ↳ AddressInput 245 | ↳ ScriptInput 246 | ↳ MultisigInput 247 | ``` 248 | 249 | All subclasses have their `Output` counterparts. 250 | 251 | 252 | #### Static methods 253 | 254 | ##### `Input.from_bytes(bytes)` 255 | Deserialize an `Input` from a binary `str`. 256 | 257 | ##### `Input.from_hex(string)` 258 | Deserialize an `Input` from a hexadecimal `str`. 259 | 260 | ##### `Input.from_buffer(buffer)` 261 | Read a serialized `Input` from a `Buffer` instance. 262 | 263 | 264 | #### Instance methods 265 | 266 | ##### `.to_bytes()` 267 | Serialize this `Input` to Bitcoin protocol format. 268 | 269 | ##### `.to_hex()` 270 | Serialize this `Input` to Bitcoin protocol format, and return it as a hexadecimal 271 | string. 272 | 273 | ##### `.replace_script(script)` 274 | Return a copy of this immutable `Input`, replacing the `script`. 275 | 276 | ##### `.remove_script()` 277 | Return a copy of this immutable `Input`, with an empty (0-byte) `script`. 278 | 279 | ##### `.sign(privkeys, payload)` 280 | Return a new `Input`, with the same `tx_id`, `txo_index` and `seq_number`. The 281 | `script` will be replaced by a version including signatures. 282 | 283 | To produce the signatures, the `payload` will be signed with all `privkeys`. 284 | 285 | `Input` **does not implement this method**, as the inner workings change with 286 | different transaction types. Subclasses provide it. 287 | 288 | 289 | #### Subclasses 290 | 291 | ##### `AddressInput(tx_id, txo_index, address, seq_number = FINAL_SEQ_NUMBER)` 292 | 293 | A _Pay-to-Pubkey-Hash_ `Input`, that can redeem funds sent to an `Address`. The 294 | `sign()` method takes a list of `privkeys` with exactly `1` key. 295 | 296 | This is the counterpart of `AddressOutput`. 297 | 298 | ```python 299 | input = AddressInput( 300 | tx_id = '4baa75...', 301 | txo_index = 0, 302 | address = PrivateKey().to_public_key().to_address() 303 | ) 304 | ``` 305 | 306 | 307 | ##### `ScriptInput(tx_id, txo_index, script, seq_number = FINAL_SEQ_NUMBER)` 308 | 309 | A _Pay-to-Script-Hash_ `Input`. `script` must be an instance of `Script`. 310 | 311 | This is the counterpart of `ScriptOutput`. 312 | 313 | ```python 314 | input = ScriptInput( 315 | tx_id = '4baa75...', 316 | txo_index = 0, 317 | script = Script(...) 318 | ) 319 | ``` 320 | 321 | 322 | ##### `MultisigInput(tx_id, txo_index, pubkeys, min_signatures, seq_number = FINAL_SEQ_NUMBER)` 323 | 324 | A special case of _Pay-to-Script-Hash_ `Input`, where `script` is internally 325 | set to a standard multi-signature `Script`. 326 | 327 | Create it with an array of `PublicKey` `pubkeys`, and specify `min_signatures`. 328 | 329 | This is the counterpart of `MultisigOutput`. 330 | 331 | ```python 332 | input = MultisigInput( 333 | tx_id = '4baa75...', 334 | txo_index = 0, 335 | pubkeys = [ PrivateKey().to_public_key(), PrivateKey().to_public_key() ], 336 | min_signatures = 1 337 | ) 338 | ``` 339 | 340 | 341 | ## Output 342 | 343 | ##### `Output(amount, script)` 344 | 345 | A `Transaction` `Output`. `amount` is an `int` of _satoshis_, `script` is a 346 | `Script` instance. 347 | 348 | The `Output` class can be instantiated directly, but the recommended approach is 349 | to use or create a subclass. For each of the `Input` subclasses described above, 350 | there is an `Output` subclass counterpart. 351 | 352 | ``` 353 | Output 354 | ↳ DataOutput 355 | ↳ AddressOutput 356 | ↳ ScriptOutput 357 | ↳ MultisigOutput 358 | ``` 359 | 360 | #### Static methods 361 | 362 | ##### `Output.from_bytes(bytes)` 363 | Deserialize an `Output` from a binary `str`. 364 | 365 | ##### `Output.from_hex(string)` 366 | Deserialize an `Output` from a hexadecimal `str`. 367 | 368 | ##### `Output.from_buffer(buffer)` 369 | Read a serialized `Output` from a `Buffer` instance. 370 | 371 | 372 | #### Instance methods 373 | 374 | ##### `.to_bytes()` 375 | Serialize this `Output` to Bitcoin protocol format. 376 | 377 | ##### `.to_hex()` 378 | Serialize this `Output` to Bitcoin protocol format, and return it as a hexadecimal 379 | string. 380 | 381 | 382 | ### Subclasses 383 | 384 | ##### `DataOutput(bytes)` 385 | 386 | A non-redeemable `OP_RETURN` `Output` that includes up to `80` `bytes` of arbitrary 387 | data in the transaction `Script`. 388 | 389 | ##### `AddressOutput(amount, address)` 390 | 391 | A _Pay-to-Pubkey-Hash_ `Output`, that can send funds to an `Address`. 392 | 393 | ##### `ScriptOutput(amount, script)` 394 | 395 | A _Pay-to-Script-Hash_ `Output`. `script` must be an instance of `Script`. 396 | 397 | ##### `MultisigOutput(amount, pubkeys, min_signatures)` 398 | 399 | A special case of _Pay-to-Script-Hash_ `Output`, where `script` is internally 400 | set to a standard multi-signature `Script`. 401 | 402 | Create it with an array of `PublicKey` `pubkeys`, and specify `min_signatures`. 403 | 404 | 405 | ## Transaction 406 | 407 | ##### `Transaction(inputs, outputs, lock_time = 0, version = 1)` 408 | 409 | A complete `Transaction`, created with a list of `Input` and `Output` instances, 410 | and optionally with `lock_time` and `version`. 411 | 412 | As all other basic objects, a `Transaction` is immutable. Modifications and 413 | signatures produce new `Transaction` instances. 414 | 415 | 416 | #### Static methods 417 | 418 | ##### `Transaction.from_bytes(bytes)` 419 | Deserialize a `Transaction` from a binary `str`. 420 | 421 | ##### `Transaction.from_hex(string)` 422 | Deserialize a `Transaction` from a hexadecimal `str`. 423 | 424 | 425 | #### Instance methods 426 | 427 | ##### `.get_id()` 428 | Return this `Transaction`'s Bitcoin ID. 429 | 430 | ##### `.to_bytes()` 431 | Serialize this `Transaction` to Bitcoin protocol format. 432 | 433 | ##### `.to_hex()` 434 | Serialize this `Transaction` to Bitcoin protocol format, and return it as a 435 | hexadecimal string. 436 | 437 | ##### `sign(privkeys, txi_index)` 438 | 439 | Return a copy of this `Transaction`, where the `Input` at `txi_index` has been 440 | signed with all `privkeys`. 441 | 442 | The method used to sign a particular `Input` is delegated to the input object 443 | itself, as implemented by the corresponding `Input` subclass. 444 | 445 | 446 | ## Development 447 | 448 | First, get the code: 449 | ``` 450 | git clone git@github.com:muun/bitforge.git; cd bitforge; 451 | ``` 452 | 453 | Then, create a new virtualenv for this project: 454 | ``` 455 | sudo pip install virtualenv 456 | virtualenv env 457 | ``` 458 | 459 | Activate virtualenv: 460 | ``` 461 | source env/bin/activate 462 | ``` 463 | 464 | Then, install bitforge's dependencies: 465 | ``` 466 | pip install -r requirements.txt 467 | ``` 468 | 469 | Run the tests to make sure everything is working: 470 | ``` 471 | py.test tests/all.py 472 | ``` 473 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 2 | - Empty string as Script.build([...]) parameter raises unreadable error 3 | - Extra signatures in multisig sign should be discarded or an error raised 4 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | 3 | matrix: 4 | - PYTHON: "C:\\Python27" 5 | - PYTHON: "C:\\Python33" 6 | - PYTHON: "C:\\Python34" 7 | - PYTHON: "C:\\Python35" 8 | - PYTHON: "C:\\Python27-x64" 9 | - PYTHON: "C:\\Python33-x64" 10 | DISTUTILS_USE_SDK: "1" 11 | - PYTHON: "C:\\Python34-x64" 12 | DISTUTILS_USE_SDK: "1" 13 | - PYTHON: "C:\\Python35-x64" 14 | 15 | build: off 16 | 17 | test_script: 18 | - "%PYTHON%\\python.exe setup.py test" 19 | -------------------------------------------------------------------------------- /bitforge/__init__.py: -------------------------------------------------------------------------------- 1 | import ecdsa 2 | from . import networks 3 | 4 | from .privkey import PrivateKey 5 | from .pubkey import PublicKey 6 | from .hdprivkey import HDPrivateKey 7 | from .hdpubkey import HDPublicKey 8 | 9 | from .address import Address 10 | from .script import Script, Opcode, Instruction 11 | 12 | from .transaction import * 13 | 14 | from .unit import Unit 15 | from .uri import URI 16 | -------------------------------------------------------------------------------- /bitforge/address.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | from enum import Enum 4 | 5 | from . import networks 6 | from .encoding import * 7 | from .errors import * 8 | from .compat import chr 9 | # from script import Script 10 | 11 | 12 | BaseAddress = collections.namedtuple('Address', ['phash', 'network', 'type']) 13 | 14 | class Address(BaseAddress): 15 | 16 | class Type(Enum): 17 | PublicKey = 'pubkeyhash' 18 | Script = 'scripthash' 19 | 20 | class Error(BitforgeError): 21 | pass 22 | 23 | class UnknownNetwork(Error, networks.UnknownNetwork): 24 | "No network for Address with an attribute '{key}' of value {value}" 25 | 26 | class InvalidVersion(Error, NumberError): 27 | "Failed to detect Address type and network from version number {number}" 28 | 29 | class InvalidBase58h(Error, InvalidBase58h): 30 | "The Address string {string} is not valid base58/check" 31 | 32 | class InvalidHex(Error, InvalidHex): 33 | "The Address string {string} is not valid hexadecimal" 34 | 35 | class InvalidHashLength(Error, StringError): 36 | "The address hash {string} should be 20 bytes long, not {length}" 37 | 38 | class InvalidBinaryLength(Error, StringError): 39 | "The binary address {string} should be 21 bytes long, not {length}" 40 | 41 | class InvalidType(Error, ObjectError): 42 | "Address type {object} is not an instance of Address.Type" 43 | 44 | 45 | def __new__(cls, phash, network = networks.default, type = Type.PublicKey): 46 | try: 47 | network = networks.find(network) 48 | except networks.UnknownNetwork: 49 | raise Address.UnknownNetwork('name', network) 50 | 51 | if not isinstance(type, Address.Type): 52 | raise Address.InvalidType(type) 53 | 54 | if len(phash) != 20: 55 | raise Address.InvalidHashLength(phash) 56 | 57 | return super(Address, cls).__new__(cls, phash, network, type) 58 | 59 | @staticmethod 60 | def from_string(string): 61 | try: 62 | bytes = decode_base58h(string) 63 | except InvalidBase58h: 64 | raise Address.InvalidBase58h(string) 65 | 66 | return Address.from_bytes(bytes) 67 | 68 | @staticmethod 69 | def from_bytes(bytes): 70 | if len(bytes) != 21: 71 | raise Address.InvalidBinaryLength(bytes) 72 | 73 | network, type = Address.classify_bytes(bytes) 74 | 75 | return Address(bytes[1:], network, type) 76 | 77 | @staticmethod 78 | def from_hex(string): 79 | try: 80 | bytes = decode_hex(string) 81 | except InvalidHex: 82 | raise Address.InvalidHex(string) 83 | 84 | return Address.from_bytes(bytes) 85 | 86 | @staticmethod 87 | def classify_bytes(bytes): 88 | version = bytearray(bytes)[0] 89 | 90 | network = networks.find(version, 'pubkeyhash', raises = False) 91 | if network is not None: 92 | return (network, Address.Type.PublicKey) 93 | 94 | network = networks.find(version, 'scripthash', raises = False) 95 | if network is not None: 96 | return (network, Address.Type.Script) 97 | 98 | raise Address.InvalidVersion(version) 99 | 100 | @staticmethod 101 | def from_public_key(pubkey): 102 | phash = ripemd160(sha256(pubkey.to_bytes())) 103 | return Address(phash, pubkey.network, Address.Type.PublicKey) 104 | 105 | @staticmethod 106 | def from_script(script, network = networks.default): 107 | phash = ripemd160(sha256(script.to_bytes())) 108 | return Address(phash, network, Address.Type.Script) 109 | 110 | def to_bytes(self): 111 | version = getattr(self.network, self.type.value) 112 | return chr(version) + self.phash 113 | 114 | def to_string(self): 115 | return encode_base58h(self.to_bytes()) 116 | 117 | def to_hex(self): 118 | return encode_hex(self.to_bytes()).decode('utf-8') 119 | 120 | def __repr__(self): 121 | return "" % (self.to_string(), self.type.name, self.network.name) 122 | -------------------------------------------------------------------------------- /bitforge/compat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | PY2 = sys.version_info[0] == 2 4 | PY3 = sys.version_info[0] == 3 5 | 6 | 7 | def chr(n): 8 | return bytes(bytearray([n])) 9 | 10 | 11 | if PY2: 12 | string_types = basestring 13 | else: 14 | string_types = str 15 | -------------------------------------------------------------------------------- /bitforge/encoding.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import binascii 3 | import hashlib 4 | 5 | from . import utils 6 | from .errors import StringError 7 | 8 | 9 | class EncodingError(StringError): 10 | "The string {string} is not properly encoded" 11 | 12 | 13 | class InvalidBase58h(EncodingError): 14 | "The string {string} is not valid base58/check" 15 | 16 | 17 | class InvalidHex(EncodingError): 18 | "The string {string} is not valid hexadecimal" 19 | 20 | 21 | class InvalidScriptNumber(EncodingError): 22 | "The script number {string} is too long" 23 | 24 | 25 | class InvalidMinimalScriptNumber(EncodingError): 26 | "The script number {string} is not minimally encoded" 27 | 28 | 29 | def encode_base58h(bytes): 30 | return utils.encoding.b2a_hashed_base58(bytes) 31 | 32 | 33 | def decode_base58h(string): 34 | try: 35 | return utils.encoding.a2b_hashed_base58(string) 36 | 37 | except utils.encoding.EncodingError: 38 | raise InvalidBase58h(string) 39 | 40 | 41 | def encode_int(integer, big_endian = True, length = None): 42 | if integer == 0: 43 | return chr(0) if length is None else chr(0) * length 44 | 45 | data = bytearray() 46 | 47 | while integer > 0: 48 | data.append(integer & 0xff) 49 | integer >>= 8 50 | 51 | if length is not None: 52 | zeros = bytearray(length) # TODO if number can't fit in length, raise 53 | data = (data + zeros)[:length] 54 | 55 | if big_endian: 56 | data.reverse() 57 | 58 | return bytes(data) 59 | 60 | 61 | def decode_int(bytes, big_endian = True): 62 | if not big_endian: 63 | bytes = reversed(bytes) 64 | 65 | integer = 0 66 | 67 | for char in bytearray(bytes): 68 | integer <<= 8 69 | integer += char 70 | 71 | return integer 72 | 73 | 74 | def encode_varint(integer): 75 | # TODO check integer is a postive number 76 | if integer < 253: 77 | return encode_int(integer) 78 | 79 | elif integer <= 0xFFFF: 80 | return chr(253) + encode_int(integer, length = 2, big_endian = False) 81 | 82 | elif integer <= 0xFFFFFFFF: 83 | return chr(254) + encode_int(integer, length = 4, big_endian = False) 84 | 85 | else: 86 | return chr(255) + encode_int(integer, length = 8, big_endian = False) 87 | 88 | # if (n < 253) { 89 | # buf = new Buffer(1); 90 | # buf.writeUInt8(n, 0); 91 | # } else if (n < 0x10000) { 92 | # buf = new Buffer(1 + 2); 93 | # buf.writeUInt8(253, 0); 94 | # buf.writeUInt16LE(n, 1); 95 | # } else if (n < 0x100000000) { 96 | # buf = new Buffer(1 + 4); 97 | # buf.writeUInt8(254, 0); 98 | # buf.writeUInt32LE(n, 1); 99 | # } else { 100 | # buf = new Buffer(1 + 8); 101 | # buf.writeUInt8(255, 0); 102 | # buf.writeInt32LE(n & -1, 1); 103 | # buf.writeUInt32LE(Math.floor(n / 0x100000000), 5); 104 | # } 105 | 106 | 107 | def encode_hex(bytes): 108 | return binascii.hexlify(bytes) 109 | 110 | 111 | def decode_hex(string): 112 | try: 113 | return binascii.unhexlify(string) 114 | except (TypeError, binascii.Error): 115 | # unhexlify() throws 2 different exceptions (length, and alphabet) 116 | raise InvalidHex(string) 117 | 118 | 119 | def sha256(bytes): 120 | return hashlib.sha256(bytes).digest() 121 | 122 | 123 | def sha1(bytes): 124 | return hashlib.sha1(bytes).digest() 125 | 126 | 127 | def ripemd160(bytes): 128 | return hashlib.new('ripemd160', bytes).digest() 129 | 130 | 131 | def hash160(bytes): 132 | return ripemd160(sha256(bytes)).digest() 133 | 134 | 135 | def decode_script_number(bytes, f_require_minimal = False, size = 4): 136 | """ 137 | Create a number from a "ScriptNum": 138 | This is analogous to the constructor for CScriptNum in bitcoind. Many ops in 139 | bitcoind's script interpreter use CScriptNum. An error is thrown if trying 140 | to input a number bigger than 4 bytes. A third argument, `size`, is provided 141 | to extend the hard limit of 4 bytes, as some usages require more than 4 bytes. 142 | """ 143 | if len(bytes) >= size: 144 | raise InvalidScriptNumber(bytes) 145 | 146 | if f_require_minimal and len(bytes) > 0: 147 | # Check the number is encoded with the minimum possible number of bytes. 148 | 149 | # If the most-significant-byte - excluding the sign bit - is zero 150 | # then we're not minimal. Note how this test also rejects the 151 | # negative-zero encoding, 0x80. 152 | if not (bytes[-1] & 0x7f): 153 | # One exception: if there's more than one byte and the most 154 | # significant bit of the second-most-significant-byte is set 155 | # it would conflict with the sign bit. An example of this case 156 | # is +-255, which encode to 0xff00 and 0xff80 respectively. 157 | # (big-endian). 158 | if len(bytes) <= 1 or not (bytes[-2] & 0x80): 159 | raise InvalidMinimalScriptNumber(bytes) 160 | 161 | if len(bytes) == 0: 162 | number = 0 163 | 164 | elif bytes[-1] & 0x80: 165 | bytes = bytes[:-1] + encode_int(bytes[-1] & 0x7f) 166 | number = decode_int(bytes, big_endian = False) * -1 167 | 168 | else: 169 | number = decode_int(bytes, big_endian = False) 170 | 171 | return number 172 | 173 | 174 | def encode_script_number(integer): 175 | if integer == 0: 176 | bytes = bytearray() 177 | 178 | elif integer > 0: 179 | bytes = bytearray(encode_int(integer, big_endian = False)) 180 | if bytes[-1] & 0x80: 181 | bytes += encode_int(0x00) 182 | 183 | else: 184 | bytes = bytearray(encode_int(-integer, big_endian = False)) 185 | if bytes[-1] & 0x80: 186 | bytes += encode_int(0x80) 187 | else: 188 | bytes = bytes[:-1] + encode_int(bytes[-1] | 0x80) 189 | 190 | return bytes 191 | -------------------------------------------------------------------------------- /bitforge/errors.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | class BitforgeError(Exception): 4 | def __init__(self, *args, **kwargs): 5 | self.cause = kwargs.pop('cause', None) 6 | self.prepare(*args, **kwargs) 7 | message = self.__doc__.format(**self.__dict__) 8 | super(BitforgeError, self).__init__(message) 9 | 10 | def prepare(self): 11 | pass 12 | 13 | def __str__(self): 14 | return self.message 15 | 16 | 17 | class ObjectError(BitforgeError): 18 | def prepare(self, object): 19 | self.object = object 20 | 21 | 22 | class StringError(BitforgeError): 23 | def prepare(self, string): 24 | self.string = repr(string) 25 | self.length = len(string) 26 | 27 | 28 | class NumberError(BitforgeError): 29 | def prepare(self, number): 30 | self.number = number 31 | 32 | 33 | class KeyValueError(BitforgeError): 34 | def prepare(self, key, value): 35 | self.key = key 36 | self.value = value 37 | -------------------------------------------------------------------------------- /bitforge/hdprivkey.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | import hashlib 4 | import hmac 5 | import os 6 | 7 | from . import ecdsa, utils, networks 8 | from .privkey import PrivateKey 9 | from .hdpubkey import HDPublicKey 10 | from .utils.intbytes import int_from_bytes, int_to_bytes, to_bytes 11 | 12 | # TODO: should be in networks.py 13 | # TODO: check which of these are network dependent 14 | MIN_SEED_LEN = 32 15 | HMAC_MAGIC_KEY = b'Bitcoin seed' 16 | ROOT_FINGERPRINT = 0 17 | HARDENED_START = 0x80000000 18 | 19 | 20 | def calculate_fingerprint(privkey): 21 | return utils.encoding.hash160(privkey.to_public_key().to_bytes())[:4] 22 | 23 | 24 | BaseHDPrivateKey = collections.namedtuple('HDPrivateKey', 25 | ['privkey', 'chain', 'depth', 'index', 'parent', 'network', 'fingerprint'] 26 | ) 27 | 28 | class HDPrivateKey(BaseHDPrivateKey): 29 | def __new__(cls, privkey, chain, depth = 0, index = 0, parent = ROOT_FINGERPRINT, network = networks.default): 30 | assert isinstance(privkey, PrivateKey) 31 | fingerprint = int_from_bytes(calculate_fingerprint(privkey)) 32 | 33 | return super(HDPrivateKey, cls).__new__(cls, privkey, chain, depth, index, parent, network, fingerprint) 34 | 35 | @staticmethod 36 | def from_seed(seed = None): 37 | if seed is None: 38 | seed = os.urandom(MIN_SEED_LEN) 39 | 40 | if len(seed) < MIN_SEED_LEN: 41 | raise ValueError("HDPrivateKey seed must be at least 32 bytes long") 42 | 43 | signed64 = hmac.new(HMAC_MAGIC_KEY, seed, hashlib.sha512).digest() 44 | 45 | return HDPrivateKey(privkey = PrivateKey.from_bytes(signed64[:32]), chain = signed64[32:]) 46 | 47 | # TODO: massage this 48 | @staticmethod 49 | def from_string(b58_str): 50 | data = utils.encoding.a2b_hashed_base58(b58_str) # TODO checksum? 51 | 52 | chain = data[HDPrivateKey.ChainCodeStart : HDPrivateKey.ChainCodeEnd] 53 | depth = int_from_bytes(data[HDPrivateKey.DepthStart : HDPrivateKey.DepthEnd]) 54 | index = int_from_bytes(data[HDPrivateKey.ChildIndexStart : HDPrivateKey.ChildIndexEnd]) 55 | parent = int_from_bytes(data[HDPrivateKey.ParentFingerPrintStart : HDPrivateKey.ParentFingerPrintEnd]) 56 | 57 | # The version field is used to deduce the network: 58 | version = int_from_bytes(data[HDPrivateKey.VersionStart:HDPrivateKey.VersionEnd]) 59 | network = networks.find(version, 'hd_private_key') 60 | privkey = PrivateKey.from_bytes(data[HDPrivateKey.PrivateKeyStart : HDPrivateKey.PrivateKeyEnd], network) 61 | 62 | return HDPrivateKey(privkey, chain, depth, index, parent, network) 63 | 64 | 65 | def to_string(self): 66 | # See BIP 32: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki 67 | bytes = ("" 68 | + to_bytes(self.network.hd_private_key, length = 4) 69 | + to_bytes(self.depth, length = 1) 70 | + to_bytes(self.parent, length = 4) 71 | + to_bytes(self.index, length = 4) 72 | + self.chain 73 | + '\0' # this zero is prepended to private keys. HDPublicKey doesn't do it 74 | + self.to_private_key().to_bytes() 75 | ) 76 | 77 | return utils.encoding.b2a_hashed_base58(bytes) 78 | 79 | 80 | def derive(self, index, hardened = False): 81 | # TODO Is index Valid? 82 | 83 | if index < HARDENED_START and hardened: 84 | index += HARDENED_START 85 | 86 | if hardened: 87 | key = '\0' + self.to_private_key().to_bytes() # a literal 0 is prepended to private keys 88 | else: 89 | key = self.to_public_key().to_bytes() 90 | 91 | signed64 = hmac.new( 92 | self.chain, 93 | key + to_bytes(self.index, length = 4), 94 | hashlib.sha512 95 | ).digest() 96 | 97 | seed = (int_from_bytes(signed64[:32]) + self.to_private_key().seed) % utils.generator_secp256k1.order() 98 | privkey = PrivateKey(seed, self.network) 99 | chain = signed64[32:] 100 | depth = self.depth + 1 101 | 102 | return HDPrivateKey(privkey, chain, depth, index, self.fingerprint, self.network) 103 | 104 | def to_hd_public_key(self): 105 | return HDPublicKey.from_hd_private_key(self) 106 | 107 | def to_private_key(self): 108 | return self.privkey 109 | 110 | def to_public_key(self): 111 | return self.to_private_key().to_public_key() 112 | 113 | 114 | HDPrivateKey.VersionSize = 4 115 | HDPrivateKey.DepthSize = 1 116 | HDPrivateKey.ParentFingerPrintSize = 4 117 | HDPrivateKey.ChildIndexSize = 4 118 | HDPrivateKey.ChainCodeSize = 32 119 | HDPrivateKey.PrivateKeySize = 32 120 | HDPrivateKey.CheckSumSize = 4 121 | 122 | HDPrivateKey.DataLength = 78 123 | HDPrivateKey.SerializedByteSize = 82 124 | 125 | HDPrivateKey.VersionStart = 0 126 | HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize 127 | HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd 128 | HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize 129 | HDPrivateKey.ParentFingerPrintStart = HDPrivateKey.DepthEnd 130 | HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize 131 | HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd 132 | HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize 133 | HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd 134 | HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize 135 | HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1 136 | HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize 137 | HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd 138 | HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize 139 | -------------------------------------------------------------------------------- /bitforge/hdpubkey.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import os, hmac, hashlib, collections 3 | 4 | from . import utils, networks 5 | from .pubkey import PublicKey 6 | from .utils.intbytes import int_from_bytes, to_bytes 7 | 8 | 9 | ROOT_FINGERPRINT = 0 10 | HARDENED_START = 0x80000000 11 | 12 | 13 | def calculate_fingerprint(pubkey): 14 | return utils.encoding.hash160(pubkey.to_bytes())[:4] 15 | 16 | BaseHDPublicKey = collections.namedtuple('HDPublicKey', 17 | ['pubkey', 'chain', 'depth', 'index', 'parent', 'network', 'fingerprint'] 18 | ) 19 | 20 | class HDPublicKey(BaseHDPublicKey): 21 | def __new__(cls, pubkey, chain, depth = 0, index = 0, parent = ROOT_FINGERPRINT, network = networks.default): 22 | assert isinstance(pubkey, PublicKey) 23 | fingerprint = int_from_bytes(calculate_fingerprint(pubkey)) 24 | 25 | return super(HDPublicKey, cls).__new__(cls, pubkey, chain, depth, index, parent, network, fingerprint) 26 | 27 | # TODO: massage this 28 | @staticmethod 29 | def from_string(b58_str): 30 | data = utils.encoding.a2b_hashed_base58(b58_str) # TODO checksum? 31 | 32 | chain = data[HDPublicKey.ChainCodeStart : HDPublicKey.ChainCodeEnd] 33 | depth = int_from_bytes(data[HDPublicKey.DepthStart : HDPublicKey.DepthEnd]) 34 | index = int_from_bytes(data[HDPublicKey.ChildIndexStart : HDPublicKey.ChildIndexEnd]) 35 | parent = int_from_bytes(data[HDPublicKey.ParentFingerPrintStart : HDPublicKey.ParentFingerPrintEnd]) 36 | 37 | # The version field is used to deduce the network: 38 | version = int_from_bytes(data[HDPublicKey.VersionStart:HDPublicKey.VersionEnd]) 39 | network = networks.find(version, 'hd_public_key') 40 | pubkey = PublicKey.from_bytes(data[HDPublicKey.PublicKeyStart : HDPublicKey.PublicKeyEnd], network) 41 | 42 | return HDPublicKey(pubkey, chain, depth, index, parent, network) 43 | 44 | # TODO: should this belong to hdprivatekey? 45 | @staticmethod 46 | def from_hd_private_key(hd_private_key): 47 | pubkey = hd_private_key.privkey.to_public_key() 48 | return HDPublicKey(pubkey, hd_private_key.chain, hd_private_key.depth, 49 | hd_private_key.index, hd_private_key.parent, hd_private_key.network) 50 | 51 | def to_string(self): 52 | # See BIP 32: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki 53 | bytes = ("" 54 | + to_bytes(self.network.hd_public_key, length = 4) 55 | + to_bytes(self.depth, length = 1) 56 | + to_bytes(self.parent, length = 4) 57 | + to_bytes(self.index, length = 4) 58 | + self.chain 59 | + self.to_public_key().to_bytes() 60 | ) 61 | 62 | return utils.encoding.b2a_hashed_base58(bytes) 63 | 64 | 65 | def derive(self, index, hardened = False): 66 | # TODO Is index Valid? 67 | 68 | if index < HARDENED_START and hardened: 69 | index += HARDENED_START 70 | 71 | if hardened: 72 | raise ValueError("Hardened derivation is not posible on HDPublicKey") 73 | else: 74 | key = self.to_public_key().to_bytes() 75 | 76 | signed64 = hmac.new( 77 | self.chain, 78 | key + to_bytes(self.index, length = 4), 79 | hashlib.sha512 80 | ).digest() 81 | 82 | x, y = self.pubkey.pair 83 | curve = utils.generator_secp256k1 84 | point = int_from_bytes(signed64[:32]) * curve + utils.Point(curve.curve(), x, y, curve.order()) 85 | pubkey = PublicKey((point.x(), point.y()), self.network) 86 | 87 | chain = signed64[32:] 88 | depth = self.depth + 1 89 | 90 | return HDPublicKey(pubkey, chain, depth, index, self.fingerprint, self.network) 91 | 92 | def to_public_key(self): 93 | return self.pubkey 94 | 95 | 96 | HDPublicKey.VersionSize = 4 97 | HDPublicKey.DepthSize = 1 98 | HDPublicKey.ParentFingerPrintSize = 4 99 | HDPublicKey.ChildIndexSize = 4 100 | HDPublicKey.ChainCodeSize = 32 101 | HDPublicKey.PublicKeySize = 33 102 | HDPublicKey.CheckSumSize = 4 103 | 104 | HDPublicKey.DataSize = 78 105 | HDPublicKey.SerializedByteSize = 82 106 | 107 | HDPublicKey.VersionStart = 0 108 | HDPublicKey.VersionEnd = HDPublicKey.VersionStart + HDPublicKey.VersionSize 109 | HDPublicKey.DepthStart = HDPublicKey.VersionEnd 110 | HDPublicKey.DepthEnd = HDPublicKey.DepthStart + HDPublicKey.DepthSize 111 | HDPublicKey.ParentFingerPrintStart = HDPublicKey.DepthEnd 112 | HDPublicKey.ParentFingerPrintEnd = HDPublicKey.ParentFingerPrintStart + HDPublicKey.ParentFingerPrintSize 113 | HDPublicKey.ChildIndexStart = HDPublicKey.ParentFingerPrintEnd 114 | HDPublicKey.ChildIndexEnd = HDPublicKey.ChildIndexStart + HDPublicKey.ChildIndexSize 115 | HDPublicKey.ChainCodeStart = HDPublicKey.ChildIndexEnd 116 | HDPublicKey.ChainCodeEnd = HDPublicKey.ChainCodeStart + HDPublicKey.ChainCodeSize 117 | HDPublicKey.PublicKeyStart = HDPublicKey.ChainCodeEnd 118 | HDPublicKey.PublicKeyEnd = HDPublicKey.PublicKeyStart + HDPublicKey.PublicKeySize 119 | HDPublicKey.ChecksumStart = HDPublicKey.PublicKeyEnd 120 | HDPublicKey.ChecksumEnd = HDPublicKey.ChecksumStart + HDPublicKey.CheckSumSize 121 | -------------------------------------------------------------------------------- /bitforge/networks.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from collections import namedtuple 3 | 4 | from .errors import KeyValueError 5 | 6 | 7 | class UnknownNetwork(KeyValueError): 8 | "No network with an attribute '{key}' of value {value}" 9 | 10 | 11 | # Network objects are immutable, and should be unique 12 | Network = namedtuple('Network', [ 13 | 'name', 14 | 15 | 'pubkeyhash', 16 | 'wif_prefix', 17 | 'scripthash', 18 | 'hd_public_key', 19 | 'hd_private_key', 20 | 'magic', 21 | 22 | 'port', 23 | 'seeds' 24 | ]) 25 | 26 | 27 | testnet = Network( 28 | name = 'testnet', 29 | 30 | pubkeyhash = 111, 31 | wif_prefix = 239, 32 | scripthash = 196, 33 | hd_public_key = 0x043587cf, 34 | hd_private_key = 0x04358394, 35 | magic = 0x0b110907, 36 | 37 | port = 18333, 38 | seeds = [ 39 | 'testnet-seed.bitcoin.petertodd.org', 40 | 'testnet-seed.bluematt.me', 41 | 'testnet-seed.alexykot.me', 42 | 'testnet-seed.bitcoin.schildbach.de' 43 | ] 44 | ) 45 | 46 | default = livenet = Network( 47 | name = 'livenet', 48 | 49 | pubkeyhash = 0x00, 50 | wif_prefix = 0x80, 51 | scripthash = 0x05, 52 | hd_public_key = 0x0488b21e, 53 | hd_private_key = 0x0488ade4, 54 | magic = 0xf9beb4d9, 55 | 56 | port = 8333, 57 | seeds = [ 58 | 'seed.bitcoin.sipa.be', 59 | 'dnsseed.bluematt.me', 60 | 'dnsseed.bitcoin.dashjr.org', 61 | 'seed.bitcoinstats.com', 62 | 'seed.bitnodes.io', 63 | 'bitseed.xf2.org' 64 | ] 65 | ) 66 | 67 | _networks = [livenet, testnet] 68 | 69 | def find(value, attr = 'name', raises = True): 70 | if isinstance(value, Network): 71 | return value 72 | 73 | try: 74 | for network in _networks: 75 | if getattr(network, attr) == value: 76 | return network 77 | 78 | except AttributeError: 79 | pass # networks don't have this attribute! 80 | 81 | if raises: 82 | raise UnknownNetwork(attr, value) 83 | -------------------------------------------------------------------------------- /bitforge/privkey.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | import random 4 | 5 | from . import networks, utils, ecdsa 6 | from .errors import * 7 | from .pubkey import PublicKey 8 | from .address import Address 9 | from .encoding import * 10 | from .compat import chr 11 | 12 | 13 | rng = random.SystemRandom() 14 | KEY_MAX = utils.generator_secp256k1.order() 15 | 16 | def random_secret(): 17 | return rng.randint(1, KEY_MAX - 1) 18 | 19 | 20 | def find_network(value, attr = 'name'): 21 | try: 22 | return networks.find(value, attr) 23 | except networks.UnknownNetwork: 24 | raise PrivateKey.UnknownNetwork(attr, value) 25 | 26 | 27 | BasePrivateKey = collections.namedtuple('PrivateKey', 28 | ['secret', 'network', 'compressed'] 29 | ) 30 | 31 | class PrivateKey(BasePrivateKey): 32 | 33 | class Error(BitforgeError): 34 | pass 35 | 36 | class InvalidSecret(Error, NumberError): 37 | "Invalid secret for PrivateKey: {number}" 38 | 39 | class UnknownNetwork(Error, networks.UnknownNetwork): 40 | "No network for PrivateKey with an attribute '{key}' of value {value}" 41 | 42 | class InvalidWifLength(Error, StringError): 43 | "The PrivateKey WIF {string} should be 33 (uncompressed) or 34 (compressed) bytes long, not {length}" 44 | 45 | class InvalidCompressionByte(Error, StringError): 46 | "The length of the PrivateKey WIF {string} suggests it's compressed, but it doesn't end in '\\1'" 47 | 48 | class InvalidBase58h(Error, InvalidBase58h): 49 | "The PrivateKey string {string} is not valid base58/check" 50 | 51 | class InvalidHex(Error, InvalidHex): 52 | "The PrivateKey string {string} is not valid hexadecimal" 53 | 54 | class InvalidBinaryLength(Error, StringError): 55 | "The PrivateKey's binary secret {string} should be 32 bytes long, not {length}" 56 | 57 | 58 | def __new__(cls, secret = None, network = networks.default, compressed = True): 59 | network = find_network(network) 60 | 61 | if secret is None: 62 | secret = random_secret() 63 | 64 | if not (0 < secret < KEY_MAX): 65 | raise PrivateKey.InvalidSecret(secret) 66 | 67 | return super(PrivateKey, cls).__new__(cls, secret, network, compressed) 68 | 69 | @staticmethod 70 | def from_wif(string): 71 | try: 72 | bytes = bytearray(decode_base58h(string)) 73 | except InvalidBase58h: 74 | raise PrivateKey.InvalidBase58h(string) 75 | 76 | if len(bytes) == 33: 77 | compressed = False 78 | 79 | elif len(bytes) == 34: 80 | if bytes[-1] != 0x01: 81 | raise PrivateKey.InvalidCompressionByte(string) 82 | 83 | bytes = bytes[:-1] 84 | compressed = True 85 | 86 | else: 87 | raise PrivateKey.InvalidWifLength(bytes) 88 | 89 | network = find_network(bytes[0], 'wif_prefix') 90 | secret = decode_int(bytes[1:]) 91 | 92 | return PrivateKey(secret, network, compressed) 93 | 94 | @staticmethod 95 | def from_bytes(bytes, network = networks.default, compressed = True): 96 | if len(bytes) != 32: 97 | raise PrivateKey.InvalidBinaryLength(bytes) 98 | 99 | secret = decode_int(bytes) 100 | return PrivateKey(secret, network, compressed) 101 | 102 | @staticmethod 103 | def from_hex(string, network = networks.default, compressed = True): 104 | try: 105 | bytes = decode_hex(string) 106 | except InvalidHex: 107 | raise PrivateKey.InvalidHex(string) 108 | 109 | return PrivateKey.from_bytes(bytes, network, compressed) 110 | 111 | def to_wif(self): 112 | network_byte = chr(self.network.wif_prefix) 113 | secret_bytes = self.to_bytes() 114 | compressed_byte = b'\1' if self.compressed else b'' 115 | 116 | return encode_base58h(network_byte + secret_bytes + compressed_byte) 117 | 118 | def to_bytes(self): 119 | return encode_int(self.secret, length = 32) 120 | 121 | def to_hex(self): 122 | return encode_hex(self.to_bytes()).decode('utf-8') 123 | 124 | def to_public_key(self): 125 | return PublicKey.from_private_key(self) 126 | 127 | def to_address(self): 128 | return Address.from_public_key(self.to_public_key()) 129 | 130 | def sign(self, message): 131 | signing_key = ecdsa.SigningKey.from_secret_exponent(self.secret, curve = ecdsa.SECP256k1) 132 | return signing_key.sign_digest(message, sigencode = ecdsa.util.sigencode_der_canonize) 133 | 134 | def verify(self, signature, message): 135 | signing_key = ecdsa.SigningKey.from_secret_exponent(self.secret, curve = ecdsa.SECP256k1) 136 | verifying_key = signing_key.get_verifying_key() 137 | 138 | return verifying_key.verify(signature, message, sigdecode = ecdsa.util.sigdecode_der) 139 | 140 | def __repr__(self): 141 | return "" % (self.to_hex(), self.network.name) 142 | -------------------------------------------------------------------------------- /bitforge/pubkey.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | 4 | from . import networks, utils 5 | from .address import Address 6 | from .encoding import * 7 | from .errors import * 8 | from .utils.secp256k1 import generator_secp256k1 9 | 10 | 11 | def find_network(value, attr = 'name'): 12 | try: 13 | return networks.find(value, attr) 14 | except networks.UnknownNetwork: 15 | raise PublicKey.UnknownNetwork(attr, value) 16 | 17 | 18 | BasePublicKey = collections.namedtuple('PublicKey', 19 | ['pair', 'network', 'compressed'] 20 | ) 21 | 22 | class PublicKey(BasePublicKey): 23 | 24 | class Error(BitforgeError): 25 | pass 26 | 27 | class InvalidPair(Error, ObjectError): 28 | "The PublicKey pair {object} is invalid (not a point of the curve)" 29 | 30 | class UnknownNetwork(Error, networks.UnknownNetwork): 31 | "No network for PublicKey with an attribute '{key}' of value {value}" 32 | 33 | class InvalidBinary(Error, StringError): 34 | "The buffer {string} is not in any recognized format" 35 | 36 | class InvalidHex(Error, InvalidHex): 37 | "The PublicKey string {string} is not valid hexadecimal" 38 | 39 | 40 | def __new__(cls, pair, network = networks.default, compressed = True): 41 | network = find_network(network) # may raise UnknownNetwork 42 | 43 | if not utils.ecdsa.is_public_pair_valid(generator_secp256k1, pair): 44 | raise PublicKey.InvalidPair(pair) 45 | 46 | return super(PublicKey, cls).__new__(cls, pair, network, compressed) 47 | 48 | @staticmethod 49 | def from_private_key(privkey): 50 | pair = utils.public_pair_for_secret_exponent( 51 | utils.generator_secp256k1, privkey.secret 52 | ) 53 | 54 | # The constructor will validate the pair 55 | return PublicKey(pair, privkey.network, privkey.compressed) 56 | 57 | @staticmethod 58 | def from_bytes(bytes, network = networks.default): 59 | try: 60 | pair = utils.encoding.sec_to_public_pair(bytes) 61 | compressed = utils.encoding.is_sec_compressed(bytes) 62 | except (utils.encoding.EncodingError, ValueError): 63 | raise PublicKey.InvalidBinary(bytes) 64 | 65 | return PublicKey(pair, network, compressed) 66 | 67 | @staticmethod 68 | def from_hex(string, network = networks.default): 69 | try: 70 | bytes = decode_hex(string) 71 | except InvalidHex: 72 | raise PublicKey.InvalidHex(string) 73 | 74 | return PublicKey.from_bytes(bytes, network) 75 | 76 | 77 | def to_bytes(self): 78 | return utils.encoding.public_pair_to_sec(self.pair, self.compressed) 79 | 80 | def to_hex(self): 81 | return binascii.hexlify(self.to_bytes()).decode('utf-8') 82 | 83 | def to_address(self): 84 | return Address.from_public_key(self) 85 | 86 | def __repr__(self): 87 | return "" % (self.to_hex(), self.network.name) 88 | -------------------------------------------------------------------------------- /bitforge/script/__init__.py: -------------------------------------------------------------------------------- 1 | from .script import Script, PayToPubkeyIn, PayToPubkeyOut, PayToScriptIn, PayToScriptOut, RedeemMultisig, OpReturnOut 2 | from .opcode import Opcode 3 | from .instruction import Instruction 4 | from .interpreter import Interpreter 5 | -------------------------------------------------------------------------------- /bitforge/script/instruction.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | 4 | from bitforge.compat import chr 5 | from bitforge.encoding import * 6 | from bitforge.errors import * 7 | from bitforge.script.opcode import * 8 | 9 | 10 | BaseInstruction = collections.namedtuple('BaseInstruction', ['opcode', 'data']) 11 | 12 | 13 | class Instruction(BaseInstruction): 14 | 15 | class Error(BitforgeError): 16 | pass 17 | 18 | class TypeError(Error, ObjectError): 19 | "Expected an Opcode, got {object}" 20 | 21 | class UnexpectedData(Error, ObjectError): 22 | "Instruction got data with opcode {object}, which does not push data" 23 | 24 | class InvalidDataLength(Error): 25 | "Opcode {opcode} can't push {data_length} bytes (max/exactly {data_length_max})" 26 | 27 | def prepare(self, opcode, data_length, data_length_max): 28 | self.opcode = opcode 29 | self.data_length = data_length 30 | self.data_length_max = data_length_max 31 | 32 | 33 | def __new__(cls, opcode, data = None): 34 | if not isinstance(opcode, Opcode): 35 | raise Instruction.TypeError(opcode) 36 | 37 | if opcode.is_push(): 38 | length = len(data) 39 | expect = Opcode.data_length_max(opcode) 40 | 41 | if (opcode.is_const_push() and length != expect) or \ 42 | (opcode.is_var_push() and not (0 <= length <= expect)): 43 | raise Instruction.InvalidDataLength(opcode, length, expect) 44 | 45 | elif data is not None: 46 | raise Instruction.UnexpectedData(opcode) 47 | 48 | return super(Instruction, cls).__new__(cls, opcode, data) 49 | 50 | @staticmethod 51 | def push_for(bytes): 52 | opcode = Opcode.push_for(len(bytes)) 53 | return Instruction(opcode, bytes) 54 | 55 | def is_push(self): 56 | return self.opcode.is_push() 57 | 58 | def is_minimal_push(self): 59 | """ 60 | Comes from bitcoind's script interpreter CheckMinimalPush function. 61 | Returns if the instruction is the smallest way to push that particular data. 62 | """ 63 | if not self.data: 64 | return True 65 | 66 | if len(self.data) == 0: 67 | # Could have used OP_0. 68 | return self.opcode == OP_0 69 | elif len(self.data) == 1 and 1 <= decode_int(self.data[0]) <= 16: 70 | # Could have used OP_1 .. OP_16 71 | return self.opcode.number == OP_1.number + decode_int(self.data[0]) - 1 72 | elif len(self.data) == 1 and decode_int(self.data[0]) == 0x81: 73 | # Could have used OP_1NEGATE 74 | return self.opcode == OP_1NEGATE 75 | elif len(self.data) <= 75: 76 | # Could have used a direct push (opcode indicating number of bytes pushed + those bytes). 77 | return self.opcode.number == len(self.data.length) 78 | elif len(self.data) <= 255: 79 | # Could have used OP_PUSHDATA. 80 | return self.opcode == OP_PUSHDATA1 81 | elif len(self.data) <= 65535: 82 | # Could have used OP_PUSHDATA2. 83 | return self.opcode == OP_PUSHDATA2 84 | 85 | return True 86 | 87 | def to_bytes(self): 88 | opcode_byte = chr(self.opcode.number) 89 | 90 | if self.opcode.is_const_push(): 91 | return opcode_byte + self.data 92 | 93 | elif self.opcode.is_var_push(): 94 | length_nbytes = Opcode.data_length_nbytes(self.opcode) 95 | 96 | length_bytes = encode_int( 97 | len(self.data), 98 | big_endian = False 99 | ).rjust(length_nbytes, '\0') 100 | 101 | return opcode_byte + length_bytes + self.data 102 | 103 | else: 104 | return opcode_byte 105 | 106 | def to_hex(self): 107 | return encode_hex(self.to_bytes()).decode('utf-8') 108 | 109 | def to_string(self): 110 | if self.opcode.is_push(): 111 | data_len = len(self.data) 112 | data_hex = u"0x" + encode_hex(self.data).decode('utf-8') 113 | 114 | if self.opcode.is_const_push(): 115 | return u"%d %s" % (data_len, data_hex) 116 | 117 | elif self.opcode.is_var_push(): 118 | return u"%s %d %s" % (self.opcode.name, data_len, data_hex) 119 | else: 120 | return self.opcode.name 121 | 122 | def __eq__(self, other): 123 | return self.opcode == other.opcode and self.data == other.data 124 | 125 | def __hash__(self): 126 | return hash((self.opcode, self.data)) 127 | 128 | def __repr__(self): 129 | if self.opcode.is_push(): 130 | return "" % (self.opcode.name, encode_hex(self.data)) 131 | else: 132 | return "" % (self.opcode.name) 133 | -------------------------------------------------------------------------------- /bitforge/script/opcode.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from functools import total_ordering 3 | import inspect 4 | import sys 5 | 6 | from bitforge.errors import * 7 | 8 | # Below is a list of all *named* opcodes. Their values, integers in the 9 | # listing, will be dynamically replaced with Opcode instances further below. 10 | 11 | # Numbers (in range [0, 16]): 12 | OP_0 = 0 13 | OP_1 = 81 14 | OP_2 = 82 15 | OP_3 = 83 16 | OP_4 = 84 17 | OP_5 = 85 18 | OP_6 = 86 19 | OP_7 = 87 20 | OP_8 = 88 21 | OP_9 = 89 22 | OP_10 = 90 23 | OP_11 = 91 24 | OP_12 = 92 25 | OP_13 = 93 26 | OP_14 = 94 27 | OP_15 = 95 28 | OP_16 = 96 29 | 30 | # Number -1: 31 | OP_1NEGATE = 79 32 | 33 | # Booleans: 34 | OP_FALSE = 0 35 | OP_TRUE = 81 36 | 37 | # Constant-length pushes, opcodes [1-75]: 38 | # These opcodes HAVE NO PROPER NAME, they are represented as their numeric 39 | # values in script. 40 | 41 | # Variable-length pushes: 42 | OP_PUSHDATA1 = 76 43 | OP_PUSHDATA2 = 77 44 | OP_PUSHDATA4 = 78 45 | 46 | # Flow control: 47 | OP_NOP = 97 48 | OP_IF = 99 49 | OP_NOTIF = 100 50 | OP_ELSE = 103 51 | OP_ENDIF = 104 52 | OP_VERIFY = 105 53 | OP_RETURN = 106 54 | 55 | # Stack operations: 56 | OP_TOALTSTACK = 107 57 | OP_FROMALTSTACK = 108 58 | OP_2DROP = 109 59 | OP_2DUP = 110 60 | OP_3DUP = 111 61 | OP_2OVER = 112 62 | OP_2ROT = 113 63 | OP_2SWAP = 114 64 | OP_IFDUP = 115 65 | OP_DEPTH = 116 66 | OP_DROP = 117 67 | OP_DUP = 118 68 | OP_NIP = 119 69 | OP_OVER = 120 70 | OP_PICK = 121 71 | OP_ROLL = 122 72 | OP_ROT = 123 73 | OP_SWAP = 124 74 | OP_TUCK = 125 75 | 76 | # String operations: 77 | OP_CAT = 126 78 | OP_SUBSTR = 127 79 | OP_LEFT = 128 80 | OP_RIGHT = 129 81 | OP_SIZE = 130 82 | 83 | # Bitwise logic: 84 | OP_INVERT = 131 85 | OP_AND = 132 86 | OP_OR = 133 87 | OP_XOR = 134 88 | OP_EQUAL = 135 89 | OP_EQUALVERIFY = 136 90 | OP_RESERVED1 = 137 91 | OP_RESERVED2 = 138 92 | 93 | # Mathematical operators: 94 | OP_1ADD = 139 95 | OP_1SUB = 140 96 | OP_2MUL = 141 97 | OP_2DIV = 142 98 | OP_NEGATE = 143 99 | OP_ABS = 144 100 | OP_NOT = 145 101 | OP_0NOTEQUAL = 146 102 | OP_ADD = 147 103 | OP_SUB = 148 104 | OP_MUL = 149 105 | OP_DIV = 150 106 | OP_MOD = 151 107 | OP_LSHIFT = 152 108 | OP_RSHIFT = 153 109 | 110 | # Comparison operators: 111 | OP_BOOLAND = 154 112 | OP_BOOLOR = 155 113 | OP_NUMEQUAL = 156 114 | OP_NUMEQUALVERIFY = 157 115 | OP_NUMNOTEQUAL = 158 116 | OP_LESSTHAN = 159 117 | OP_GREATERTHAN = 160 118 | OP_LESSTHANOREQUAL = 161 119 | OP_GREATERTHANOREQUAL = 162 120 | OP_MIN = 163 121 | OP_MAX = 164 122 | OP_WITHIN = 165 123 | 124 | # Cryptography: 125 | OP_RIPEMD160 = 166 126 | OP_SHA1 = 167 127 | OP_SHA256 = 168 128 | OP_HASH160 = 169 129 | OP_HASH256 = 170 130 | OP_CODESEPARATOR = 171 131 | OP_CHECKSIG = 172 132 | OP_CHECKSIGVERIFY = 173 133 | OP_CHECKMULTISIG = 174 134 | OP_CHECKMULTISIGVERIFY = 175 135 | 136 | # Locktime: 137 | OP_CHECKLOCKTIMEVERIFY = 177 138 | 139 | # Ignored operations: 140 | OP_NOP1 = 176 141 | OP_NOP3 = 178 142 | OP_NOP4 = 179 143 | OP_NOP5 = 180 144 | OP_NOP6 = 181 145 | OP_NOP7 = 182 146 | OP_NOP8 = 183 147 | OP_NOP9 = 184 148 | OP_NOP10 = 185 149 | 150 | # Internal operations (invalid if found in script): 151 | OP_PUBKEYHASH = 253 152 | OP_PUBKEY = 254 153 | OP_INVALIDOPCODE = 255 154 | OP_RESERVED = 80 155 | 156 | 157 | @total_ordering 158 | class Opcode(object): 159 | 160 | class Error(BitforgeError): 161 | pass 162 | 163 | class UnknownOpcodeName(Error, StringError): 164 | "No known operation named {string}" 165 | 166 | class UnknownOpcodeNumber(Error, NumberError): 167 | "No known operation numbered {number}" 168 | 169 | class InvalidConstPushLength(Error, NumberError): 170 | "No constant push opcode can push {number} bytes (only [1-75])" 171 | 172 | class InvalidPushLength(Error, NumberError): 173 | "No Opcode can push {number} bytes" 174 | 175 | class TypeError(Error, ObjectError): 176 | "Opcodes are initialized from numbers and names, got object {object}" 177 | 178 | class WrongOpcodeType(Error, ObjectError): 179 | "Opcode {object.name} does not support this operation" 180 | 181 | opcode_number_to_name = {} # Filled after class definition 182 | 183 | 184 | def __init__(self, number): 185 | if not (0 <= number <= 255): 186 | raise Opcode.UnknownOpcodeNumber(number) 187 | 188 | self.number = number 189 | 190 | @property 191 | def name(self): 192 | if self.is_const_push(): 193 | return "_PUSH_%d_BYTES" % self.number 194 | else: 195 | return Opcode.opcode_number_to_name[self.number] 196 | 197 | def is_number(self): 198 | return self == OP_0 or (OP_1 <= self <= OP_16) 199 | 200 | def number_value(self): 201 | if not self.is_number(): 202 | raise Opcode.WrongOpcodeType(self) 203 | 204 | return 0 if self == OP_0 else (self.number - OP_1.number + 1) 205 | 206 | def is_push(self): 207 | return self.is_const_push() or self.is_var_push() 208 | 209 | def is_const_push(self): 210 | # Opcodes with `value` in the range [1, 75] push `value` bytes onto 211 | # the stack 212 | return 1 <= self.number <= 75 213 | 214 | def is_var_push(self): 215 | # PUSHDATA1, PUSHDATA2, and PUSHDATA4 push `n` bytes onto the stack, 216 | # where `n` is the integer in the 1/2/4 bytes following the opcode 217 | return self in (OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4) 218 | 219 | def __repr__(self): 220 | if self.is_const_push(): 221 | return "" % self.number 222 | else: 223 | return "" % (self.number, self.name) 224 | 225 | def __eq__(self, other): 226 | if not isinstance(other, Opcode): 227 | return False 228 | 229 | return self.number == other.number 230 | 231 | def __lt__(self, other): 232 | if not isinstance(other, Opcode): 233 | return False 234 | 235 | return self.number < other.number 236 | 237 | def __hash__(self): 238 | return hash(self.number) 239 | 240 | @property 241 | def bytes(self): 242 | return bytes(bytearray([self.number])) 243 | 244 | @staticmethod 245 | def for_number(n): 246 | if 0 <= n <= 16: 247 | return OP_0 if n == 0 else Opcode(OP_1.number + n - 1) 248 | else: 249 | raise ValueError("Expected number in range [0, 16], got %d" % n) 250 | 251 | @staticmethod 252 | def from_name(name): 253 | if not name.startswith('OP_'): 254 | raise Opcode.UnknownOpcodeName(name) 255 | 256 | # Get element from module 257 | _module = sys.modules[__name__] 258 | members = inspect.getmembers(_module) 259 | # Cast iterable to list for python 3 compatibility 260 | results = list(filter(lambda member: member[0] == name, members)) 261 | 262 | if not results: 263 | raise Opcode.UnknownOpcodeName(name) 264 | 265 | return results[0][1] 266 | 267 | @staticmethod 268 | def const_push_for(length): 269 | if not (1 <= length <= 75): 270 | raise Opcode.InvalidConstPushLength(length) 271 | 272 | return Opcode(length) 273 | 274 | @staticmethod 275 | def var_push_for(length): 276 | if length < 1: 277 | raise Opcode.InvalidPushLength(length) 278 | 279 | for opcode in [OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4]: 280 | if length <= Opcode.data_length_max(opcode): 281 | return opcode 282 | 283 | raise Opcode.InvalidPushLength(length) 284 | 285 | @staticmethod 286 | def push_for(length): 287 | if length <= 75: 288 | return Opcode.const_push_for(length) 289 | else: 290 | return Opcode.var_push_for(length) 291 | 292 | @staticmethod 293 | def data_length_max(opcode): 294 | if opcode.is_const_push(): 295 | return opcode.number 296 | 297 | elif opcode.is_var_push(): 298 | return { 299 | OP_PUSHDATA1: 2 ** 8, 300 | OP_PUSHDATA2: 2 ** 16, 301 | OP_PUSHDATA4: 2 ** 32 302 | }[opcode] - 1 303 | 304 | else: 305 | return 0 306 | 307 | @staticmethod 308 | def data_length_nbytes(opcode): 309 | if not opcode.is_var_push(): 310 | raise Opcode.WrongOpcodeType(opcode) 311 | 312 | return { 313 | OP_PUSHDATA1: 1, 314 | OP_PUSHDATA2: 2, 315 | OP_PUSHDATA4: 4 316 | }[opcode] 317 | 318 | 319 | 320 | # Walk the OP_* variables, mapping them to their names and creating Opcode objs: 321 | _module = sys.modules[__name__] 322 | 323 | for name, number in inspect.getmembers(_module): 324 | if name.startswith('OP_'): 325 | # Populate the reverse opcode-number-to-name map: 326 | Opcode.opcode_number_to_name[number] = name 327 | 328 | # Replace integer values with actual Opcode instances: 329 | setattr(_module, name, Opcode(number)) 330 | 331 | Opcode.opcode_number_to_name[0] = 'OP_0' # shares number with OP_FALSE 332 | Opcode.opcode_number_to_name[81] = 'OP_1' # shares number with OP_TRUE 333 | 334 | del _module # the expected use for this module is to import * 335 | -------------------------------------------------------------------------------- /bitforge/script/script.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from numbers import Number 3 | import collections 4 | 5 | from bitforge.encoding import * 6 | from bitforge.tools import Buffer 7 | from bitforge.errors import * 8 | from bitforge import PublicKey 9 | 10 | from .opcode import * 11 | from .instruction import Instruction 12 | 13 | 14 | BaseScript = collections.namedtuple('Script', 15 | ['instructions'] 16 | ) 17 | 18 | 19 | class Script(BaseScript): 20 | 21 | class Error(BitforgeError): 22 | pass 23 | 24 | class MissingPushArguments(Error, StringError): 25 | "Missing arguments for {string} operation" 26 | 27 | class InvalidPushSize(Error, StringError): 28 | "Push size must be a number, got {string}" 29 | 30 | class InvalidPushData(Error, StringError): 31 | "Push data must be hex encoded and start with 0x, got {string}" 32 | 33 | class InvalidPushDataLength(Error, NumberError): 34 | "Push data length doesn't match push size, got {number}" 35 | 36 | class UnknownOpcodeName(Error, StringError): 37 | "No known operation named {string}" 38 | 39 | def __new__(cls, instructions = None): 40 | instructions = tuple(instructions if instructions is not None else []) 41 | return super(Script, cls).__new__(cls, instructions) 42 | 43 | def get_structure(self): 44 | return tuple(i.opcode if not i.is_push() else 'PUSH' for i in self.instructions) 45 | 46 | @staticmethod 47 | def create(instructions): 48 | generic = Script(instructions) 49 | subcls = Script.classify(generic) 50 | return subcls(instructions) if subcls else generic 51 | 52 | @staticmethod 53 | def classify(script): 54 | for subcls in SCRIPT_SUBCLASSES: 55 | if subcls.is_valid(script): 56 | return subcls 57 | 58 | return None 59 | 60 | @staticmethod 61 | def from_bytes(bytes): 62 | buffer = Buffer(bytes) 63 | return Script.from_buffer(buffer) 64 | 65 | @staticmethod 66 | def from_buffer(buffer): 67 | instructions = [] 68 | 69 | while buffer: 70 | opcode = Opcode(ord(buffer.read(1))) 71 | data = None 72 | 73 | if opcode.is_const_push(): 74 | data = buffer.read(opcode.number) 75 | 76 | elif opcode.is_var_push(): 77 | length_bytes = buffer.read(Opcode.data_length_nbytes(opcode)) 78 | 79 | length = decode_int(length_bytes, big_endian = False) 80 | data = buffer.read(length) 81 | 82 | instructions.append(Instruction(opcode, data)) 83 | 84 | return Script.create(instructions) 85 | 86 | @staticmethod 87 | def from_string(string): 88 | instructions = [] 89 | tokens = (i for i in string.split(' ')) 90 | 91 | def get_opcode(token): 92 | try: 93 | return Opcode.from_name(token) 94 | except Opcode.UnknownOpcodeName: 95 | if token.isdigit(): 96 | return Opcode.const_push_for(int(token)) 97 | else: 98 | raise Script.UnknownOpcodeName(token) 99 | 100 | for token in tokens: 101 | try: 102 | opcode = get_opcode(token) 103 | 104 | if opcode.is_const_push(): 105 | hex_bytes = next(tokens) 106 | 107 | if not hex_bytes.startswith('0x'): 108 | raise Script.InvalidPushData(hex_bytes) 109 | 110 | bytes = decode_hex(hex_bytes[2:]) 111 | instructions.append(Instruction(opcode, data=bytes)) 112 | 113 | elif opcode.is_var_push(): 114 | size_string = next(tokens) 115 | hex_bytes = next(tokens) 116 | 117 | if not size_string.isdigit(): 118 | raise Script.InvalidPushSize(size_string) 119 | 120 | if not hex_bytes.startswith('0x'): 121 | raise Script.InvalidPushData(hex_bytes) 122 | 123 | bytes = decode_hex(hex_bytes[2:]) 124 | 125 | if int(size_string) != len(bytes): 126 | raise Script.InvalidPushDataLength(len(bytes)) 127 | 128 | instructions.append(Instruction(opcode, data = bytes)) 129 | 130 | else: 131 | instructions.append(Instruction(opcode)) 132 | 133 | except StopIteration: 134 | raise Script.MissingPushArguments(token) 135 | 136 | return Script.create(instructions) 137 | 138 | @staticmethod 139 | def compile(schematic): 140 | return Script.create(to_instructions(schematic)) 141 | 142 | def push_data(self, bytes): 143 | instruction = Instruction.push_for(bytes) 144 | self.instructions.push(instruction) 145 | 146 | def remove_opcode_by_data(self, bytes): 147 | instruction = Instruction.push_for(bytes) 148 | self.instructions = filter(lambda i: i == instruction, self.instructions) 149 | 150 | def is_push_only(self): 151 | """ 152 | :returns: if the script is only composed of data pushing opcodes or small int opcodes (OP_0, OP_1, ..., OP_16) 153 | """ 154 | return all(i.opcode <= OP_16 for i in self.instructions) 155 | 156 | def __repr__(self): 157 | return str(self.instructions) 158 | 159 | def to_bytes(self): 160 | data = bytearray() 161 | for i in self.instructions: data += i.to_bytes() 162 | return bytes(data) 163 | 164 | def to_hex(self): 165 | return encode_hex(self.to_bytes()).decode('utf-8') 166 | 167 | def to_hash(self): 168 | return ripemd160(sha256(self.to_bytes())) 169 | 170 | def to_string(self): 171 | return ' '.join(i.to_string() for i in self.instructions) 172 | 173 | 174 | class PayToPubkeyIn(Script): 175 | 176 | @classmethod 177 | def create(cls, pubkey, signature): 178 | schematic = [ signature, pubkey.to_bytes() ] 179 | return cls(to_instructions(schematic)) 180 | 181 | @staticmethod 182 | def is_valid(script): 183 | return script.get_structure() == ('PUSH', 'PUSH') 184 | 185 | def get_public_key(self): 186 | return PublicKey.from_bytes(self.instructions[1].data) 187 | 188 | def get_signature(self): 189 | return self.instructions[0].data 190 | 191 | 192 | class PayToPubkeyOut(Script): 193 | 194 | @classmethod 195 | def create(cls, address): 196 | schematic = [ OP_DUP, OP_HASH160, address.phash, OP_EQUALVERIFY, OP_CHECKSIG ] 197 | return cls(to_instructions(schematic)) 198 | 199 | @staticmethod 200 | def is_valid(script): 201 | return script.get_structure() == (OP_DUP, OP_HASH160, 'PUSH', OP_EQUALVERIFY, OP_CHECKSIG) 202 | 203 | def get_address_hash(self): 204 | return self.instructions[2].data 205 | 206 | 207 | 208 | class PayToScriptIn(Script): 209 | 210 | @classmethod 211 | def create(cls, script, signatures): 212 | schematic = [OP_0] + signatures + [script.to_bytes()] 213 | return cls(to_instructions(schematic)) 214 | 215 | @staticmethod 216 | def is_valid(script): 217 | structure = script.get_structure() 218 | return ( 219 | len(structure) > 2 and 220 | structure[0] == OP_0 and 221 | all(op == 'PUSH' for op in structure[1:]) 222 | ) 223 | 224 | def get_script(self): 225 | return Script.from_bytes(self.instructions[-1].data) 226 | 227 | def get_signatures(self): 228 | return [ i.data for i in self.instructions[1:-1] ] 229 | 230 | 231 | 232 | class PayToScriptOut(Script): 233 | 234 | @classmethod 235 | def create(cls, script): 236 | schematic = [ OP_HASH160, script.to_hash(), OP_EQUAL ] 237 | return cls(to_instructions(schematic)) 238 | 239 | @staticmethod 240 | def is_valid(script): 241 | return script.get_structure() == (OP_HASH160, 'PUSH', OP_EQUAL) 242 | 243 | def get_script_hash(self): 244 | return self.instructions[1].data 245 | 246 | 247 | class OpReturnOut(Script): 248 | 249 | @classmethod 250 | def create(cls, data): 251 | schematic = [ OP_RETURN, data ] 252 | return cls(to_instructions(schematic)) 253 | 254 | @staticmethod 255 | def is_valid(script): 256 | return script.get_structure() == (OP_RETURN, 'PUSH') 257 | 258 | def get_data(self): 259 | return self.instructions[1].data 260 | 261 | 262 | class RedeemMultisig(Script): 263 | 264 | @classmethod 265 | def create(cls, pubkeys, min_signatures): 266 | schematic = ( 267 | [ Opcode.for_number(min_signatures) ] + 268 | [ pubkey.to_bytes() for pubkey in pubkeys ] + 269 | [ Opcode.for_number(len(pubkeys)) ] + 270 | [ OP_CHECKMULTISIG ] 271 | ) 272 | 273 | return cls(to_instructions(schematic)) 274 | 275 | @staticmethod 276 | def is_valid(script): 277 | structure = script.get_structure() 278 | 279 | return ( 280 | len(structure) >= 4 and 281 | structure[0].is_number() and 282 | structure[-2].is_number() and 283 | structure[-1] == OP_CHECKMULTISIG and 284 | all(op == 'PUSH' for op in structure[1:-2]) 285 | ) 286 | 287 | def get_min_signatures(self): 288 | return self.instructions[0].opcode.number_value() 289 | 290 | def get_public_keys(self): 291 | return [ PublicKey.from_bytes(i.data) for i in self.instructions[1:-2] ] 292 | 293 | 294 | SCRIPT_SUBCLASSES = [ 295 | PayToPubkeyIn, 296 | PayToPubkeyOut, 297 | PayToScriptIn, 298 | PayToScriptOut, 299 | OpReturnOut, 300 | RedeemMultisig, 301 | ] 302 | 303 | 304 | def to_instructions(schematic): 305 | instructions = [] 306 | 307 | for item in schematic: 308 | if isinstance(item, Opcode): 309 | args = (item,) 310 | 311 | elif isinstance(item, Number): 312 | args = (Opcode(item),) 313 | 314 | elif isinstance(item, bytes): 315 | args = (Opcode.push_for(len(item)), item) 316 | 317 | elif isinstance(item, tuple): 318 | if isinstance(item[0], Opcode): 319 | args = tuple(item) 320 | else: 321 | args = (Opcode(item[0]), item[1]) 322 | 323 | else: 324 | raise TypeError("Can't auto-create an Instruction from object " + repr(item)) 325 | 326 | instructions.append(Instruction(*args)) 327 | 328 | return instructions 329 | -------------------------------------------------------------------------------- /bitforge/signature.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | 4 | SIGHASH_ALL = 0x01 5 | SIGHASH_NONE = 0x02 6 | SIGHASH_SINGLE = 0x03 7 | SIGHASH_ANYONECANPAY = 0x80 8 | 9 | 10 | def validate_signature(sig): 11 | # Minimum and maximum size constraints. 12 | sig = map(ord, sig) 13 | 14 | if (len(sig) < 9): return False; 15 | if (len(sig) > 73): return False; 16 | 17 | # A signature is of type 0x30 (compound). 18 | if (sig[0] != 0x30): return False; 19 | 20 | # Make sure the length covers the entire signature. 21 | if (sig[1] != len(sig) - 3): return False; 22 | 23 | # Extract the length of the R element. 24 | lenR = sig[3] 25 | 26 | # Make sure the length of the S element is still inside the signature. 27 | if (5 + lenR >= len(sig)): return False; 28 | 29 | # Extract the length of the S element. 30 | lenS = sig[5 + lenR]; 31 | 32 | # Verify that the length of the signature matches the sum of the length 33 | # of the elements. 34 | if ((lenR + lenS + 7) != len(sig)): return False; 35 | 36 | # Check whether the R element is an integer. 37 | if (sig[2] != 0x02): return False; 38 | 39 | # Zero-length integers are not allowed for R. 40 | if (lenR == 0): return False; 41 | 42 | # Negative numbers are not allowed for R. 43 | if (sig[4] & 0x80): return False; 44 | 45 | # Null bytes at the start of R are not allowed, unless R would 46 | # otherwise be interpreted as a negative number. 47 | if (lenR > 1 and (sig[4] == 0x00) and not (sig[5] & 0x80)): 48 | return False; 49 | 50 | # Check whether the S element is an integer. 51 | if (sig[lenR + 4] != 0x02): return False; 52 | 53 | # Zero-length integers are not allowed for S. 54 | if (lenS == 0): return False; 55 | 56 | # Negative numbers are not allowed for S. 57 | if (sig[lenR + 6] & 0x80): return False; 58 | 59 | # Null bytes at the start of S are not allowed, unless S would otherwise be 60 | # interpreted as a negative number. 61 | if (lenS > 1 and (sig[lenR + 6] == 0x00) and not (sig[lenR + 7] & 0x80)): return False; 62 | 63 | return True; 64 | -------------------------------------------------------------------------------- /bitforge/tools.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from .errors import * 4 | from .encoding import * 5 | 6 | 7 | class Buffer(bytearray): 8 | 9 | class Error(BitforgeError): 10 | pass 11 | 12 | class InsufficientData(Error): 13 | "Attempted to read {requested} bytes, but buffer only has {remaining}" 14 | 15 | def prepare(self, remaining, requested): 16 | self.remaining = remaining 17 | self.requested = requested 18 | 19 | def read(self, amount): 20 | if len(self) < amount: 21 | raise Buffer.InsufficientData(len(self), amount) 22 | 23 | ret = self[:amount] 24 | del self[:amount] 25 | return ret 26 | 27 | def read_varint(self): 28 | order = decode_int(self.read(1)) 29 | 30 | if order < 253: 31 | return order # single-byte varint, value is as written 32 | 33 | elif order == 253: 34 | return decode_int(self.read(2), big_endian = False) 35 | 36 | elif order == 254: 37 | return decode_int(self.read(4), big_endian = False) 38 | 39 | elif order == 255: 40 | return decode_int(self.read(8), big_endian = False) 41 | 42 | def write(self, data): 43 | self.extend(data) 44 | 45 | 46 | def enforce(object, predicate, ExceptionClass): 47 | if not predicate(object): 48 | raise ExceptionClass(object) 49 | 50 | 51 | def enforce_all(objects, predicate, ExceptionClass): 52 | for object in objects: 53 | enforce(object, predicate, ExceptionClass) 54 | 55 | 56 | def instance_of(Class): 57 | def is_instance_of(object): 58 | return isinstance(object, Class) 59 | 60 | return is_instance_of 61 | 62 | 63 | # def has_length(min, max, ExceptionClass): 64 | # def object_has_length(object): 65 | # length = len(object) 66 | # return (min is None or length >= min) and (max is None or length <= max) 67 | # 68 | # return object_has_length 69 | -------------------------------------------------------------------------------- /bitforge/transaction/__init__.py: -------------------------------------------------------------------------------- 1 | from .input import Input, AddressInput, ScriptInput, MultisigInput 2 | from .output import Output, AddressOutput, ScriptOutput, MultisigOutput, DataOutput 3 | from .transaction import Transaction 4 | -------------------------------------------------------------------------------- /bitforge/transaction/input.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | 4 | from bitforge.encoding import * 5 | from bitforge.errors import * 6 | from bitforge.tools import Buffer 7 | from bitforge.signature import SIGHASH_ALL 8 | from bitforge.script import Script, PayToPubkeyIn, PayToScriptIn, RedeemMultisig, PayToPubkeyOut 9 | 10 | 11 | FINAL_SEQ_NUMBER = 0xFFFFFFFF 12 | 13 | 14 | BaseInput = collections.namedtuple('Input', 15 | ['tx_id', 'txo_index', 'script', 'seq_number'] 16 | ) 17 | 18 | 19 | class Input(BaseInput): 20 | 21 | class Error(BitforgeError): 22 | pass 23 | 24 | class UnknownSignatureMethod(Error): 25 | "This abstract Input doesn't know how to sign itself. You should use an Input subclass, such as AddressInput" 26 | 27 | class InvalidSignatureCount(Error): 28 | "This Input requires {required} keys to sign, but {provided} were provided" 29 | 30 | def prepare(self, required_keys, provided_keys): 31 | self.required = required_keys 32 | self.provided = provided_keys 33 | 34 | 35 | def __new__(cls, tx_id, txo_index, script, seq_number = FINAL_SEQ_NUMBER): 36 | # TODO validation 37 | return super(Input, cls).__new__(cls, tx_id, txo_index, script, seq_number) 38 | 39 | @classmethod 40 | def create(cls, tx_id, txo_index, script, seq_number = FINAL_SEQ_NUMBER): 41 | script = Script.create(script.instructions) # classified copy 42 | args = (tx_id, txo_index, script, seq_number) 43 | 44 | if isinstance(script, PayToPubkeyIn): 45 | return AddressInput(*args) 46 | 47 | elif isinstance(script, PayToScriptIn): 48 | return ScriptInput(*args) 49 | 50 | elif isinstance(script, OpReturnOut): 51 | return DataOutput(*args) 52 | 53 | else: 54 | return Output(amount, script) 55 | 56 | def to_bytes(self): 57 | buffer = Buffer() 58 | script = self.script.to_bytes() 59 | 60 | # Reverse transaction ID (double SHA256 hex of previous tx) (32 bytes): 61 | buffer.write(reversed(decode_hex(self.tx_id))) 62 | 63 | # Previous tx output index, as little-endian uint32 (4 bytes): 64 | buffer.write(encode_int(self.txo_index, length = 4, big_endian = False)) 65 | 66 | # Script length, as variable-length integer (1-9 bytes): 67 | buffer.write(encode_varint(len(script))) 68 | 69 | # Script body (? bytes): 70 | buffer.write(script) 71 | 72 | # Sequence number, as little-endian uint32 (4 bytes): 73 | buffer.write(encode_int(self.seq_number, length = 4, big_endian = False)) 74 | 75 | return str(buffer) 76 | 77 | def to_hex(self): 78 | return encode_hex(self.to_bytes()).decode('utf-8') 79 | 80 | def replace_script(self, script): 81 | return Input(self.tx_id, self.txo_index, script, self.seq_number) 82 | 83 | def remove_script(self): 84 | return self.replace_script(Script()) 85 | 86 | def sign(self, privkeys, payload, sigtype = SIGHASH_ALL): 87 | # Signing an Input requires knowledge of two things: 88 | # 89 | # 1. The placeholder Script that will be used in place of the signed one, 90 | # to construct the actual signature (it can't sign itself) 91 | # 92 | # 2. The method with which to construct the final, signed Script 93 | # 94 | # By the time this method is invoked, the placeholder script (1) should 95 | # already be waiting in our `script` property, but (2) we can't know 96 | # about. See Input subclasses. 97 | raise Input.UnknownSignatureMethod() 98 | 99 | @classmethod 100 | def from_hex(cls, string): 101 | return cls.from_bytes(decode_hex(string)) 102 | 103 | @classmethod 104 | def from_bytes(cls, bytes): 105 | return cls.from_buffer(Buffer(bytes)) 106 | 107 | @classmethod 108 | def from_buffer(cls, buffer): 109 | # Inverse operation of Input.to_bytes(), check that out. 110 | tx_id = encode_hex(buffer.read(32)[::-1]) # reversed 111 | txo_index = decode_int(buffer.read(4), big_endian = False) 112 | 113 | script_len = buffer.read_varint() 114 | script = Script.from_bytes(buffer.read(script_len)) 115 | 116 | seq_number = decode_int(buffer.read(4), big_endian = False) 117 | 118 | return cls(tx_id, txo_index, script, seq_number) 119 | 120 | 121 | class AddressInput(Input): 122 | 123 | @classmethod 124 | def create(cls, tx_id, txo_index, address, seq_number = FINAL_SEQ_NUMBER): 125 | # The placeholder Script for an AddressInput (Pay-to-Pubkey, in raw 126 | # Bitcoin terms) is a copy of the UTXO Script from the previous 127 | # transaction. Assuming that Output had a standard Pay-to-Pubkey Script, 128 | # we don't need to actually fetch the data. 129 | placeholder_script = PayToPubkeyOut.create(address) 130 | 131 | return cls(tx_id, txo_index, placeholder_script, seq_number) 132 | 133 | def sign(self, privkeys, payload, sigtype = SIGHASH_ALL): 134 | if len(privkeys) != 1: 135 | raise AddressInput.InvalidSignatureCount(1, len(privkeys)) 136 | 137 | signed_script = PayToPubkeyIn.create( 138 | pubkey = privkeys[0].to_public_key(), 139 | signature = privkeys[0].sign(payload) + chr(sigtype) 140 | ) 141 | 142 | return self.replace_script(signed_script) 143 | 144 | def can_sign(self, privkeys): 145 | return ( 146 | len(privkeys) == 1 and 147 | privkeys[0].to_address().phash == self.script.get_address_hash() 148 | ) 149 | 150 | 151 | class ScriptInput(Input): 152 | 153 | @classmethod 154 | def create(cls, tx_id, txo_index, script, seq_number = FINAL_SEQ_NUMBER): 155 | # The placeholder Script for a ScriptInput (Pay-to-Script, in raw 156 | # Bitcoin terms) is the embedded (redeeming) Script itself. 157 | 158 | return cls(tx_id, txo_index, script, seq_number) 159 | 160 | def sign(self, privkeys, payload, sigtype = SIGHASH_ALL): 161 | # Signing a ScriptInput requires embedding the redeem Script (already 162 | # set as placeholder in our `script` property by the time this method 163 | # is invoked) in a standard Pay-to-Script Script. 164 | signed_script = PayToScriptIn.create( 165 | script = self.script, 166 | signatures = [ pk.sign(payload) + chr(sigtype) for pk in privkeys ] 167 | ) 168 | 169 | return self.replace_script(signed_script) 170 | 171 | 172 | class MultisigInput(ScriptInput): 173 | 174 | @classmethod 175 | def create(cls, tx_id, txo_index, pubkeys, min_signatures, seq_number = FINAL_SEQ_NUMBER): 176 | # There is nothing magical about a MultisigInput. All we need to do 177 | # is construct the placeholder Script for the ScriptInput automatically, 178 | # since we know the form it will take. 179 | placeholder_script = RedeemMultisig.create(pubkeys, min_signatures) 180 | 181 | return cls(tx_id, txo_index, placeholder_script, seq_number) 182 | 183 | def can_sign(self, privkeys): 184 | if len(privkeys) != self.script.get_min_signatures(): 185 | return False 186 | 187 | expected_pubkeys = self.script.get_public_keys() 188 | 189 | for privkey in privkeys: 190 | if privkey.to_public_key() not in expected_pubkeys: 191 | return False 192 | 193 | return True 194 | -------------------------------------------------------------------------------- /bitforge/transaction/output.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | 4 | from bitforge.encoding import * 5 | from bitforge.errors import * 6 | from bitforge.tools import Buffer 7 | from bitforge.script import Script, PayToPubkeyOut, PayToScriptOut, RedeemMultisig, OpReturnOut 8 | 9 | 10 | BaseOutput = collections.namedtuple('Output', 11 | ['amount', 'script'] 12 | ) 13 | 14 | 15 | class Output(BaseOutput): 16 | 17 | class Error(BitforgeError): 18 | pass 19 | 20 | def __new__(cls, amount, script): 21 | # TODO validation 22 | return super(Output, cls).__new__(cls, amount, script) 23 | 24 | @classmethod 25 | def create(cls, amount, script): 26 | script = Script.create(script.instructions) # classified copy 27 | 28 | if isinstance(script, PayToPubkeyOut): 29 | return AddressOutput(amount, script) 30 | 31 | elif isinstance(script, PayToScriptOut): 32 | return ScriptOutput(amount, script) 33 | 34 | elif isinstance(script, OpReturnOut): 35 | return DataOutput(amount, script) 36 | 37 | else: 38 | return Output(amount, script) 39 | 40 | def to_bytes(self): 41 | buffer = Buffer() 42 | script = self.script.to_bytes() 43 | 44 | # Output amount in Satoshis, as little-endian uint64 (8 bytes): 45 | buffer.write(encode_int(self.amount, length = 8, big_endian = False)) 46 | 47 | # Script length, as variable-length integer (1-9 bytes): 48 | buffer.write(encode_varint(len(script))) 49 | 50 | # Script body (? bytes): 51 | buffer.write(script) 52 | 53 | return str(buffer) 54 | 55 | def to_hex(self): 56 | return encode_hex(self.to_bytes()).decode('utf-8') 57 | 58 | @staticmethod 59 | def from_hex(string): 60 | return Output.from_bytes(decode_hex(string)) 61 | 62 | @staticmethod 63 | def from_bytes(bytes): 64 | return Output.from_buffer(Buffer(bytes)) 65 | 66 | @staticmethod 67 | def from_buffer(buffer): 68 | # Inverse operation of Output.to_bytes(), check that out. 69 | amount = decode_int(buffer.read(8), big_endian = False) 70 | 71 | script_len = buffer.read_varint() 72 | script = Script.from_bytes(buffer.read(script_len)) 73 | 74 | return Output.create(amount, script) 75 | 76 | 77 | class AddressOutput(Output): 78 | 79 | @classmethod 80 | def create(cls, amount, address): 81 | script = PayToPubkeyOut.create(address) 82 | return cls(amount, script) 83 | 84 | 85 | class ScriptOutput(Output): 86 | 87 | @classmethod 88 | def create(cls, amount, redeem_script): 89 | script = PayToScriptOut.create(redeem_script) 90 | return cls(amount, script) 91 | 92 | 93 | class MultisigOutput(ScriptOutput): 94 | 95 | @classmethod 96 | def create(cls, amount, pubkeys, min_signatures): 97 | redeem_script = RedeemMultisig.create(pubkeys, min_signatures) 98 | return cls(amount, redeem_script) 99 | 100 | 101 | class DataOutput(Output): 102 | 103 | class TooMuchData(Output.Error, NumberError): 104 | "DataOutputs can carry at most 80 bytes, but {number} were passed in" 105 | 106 | @classmethod 107 | def create(cls, data): 108 | if len(data) > 80: 109 | raise DataOutput.TooMuchData(len(data)) 110 | 111 | amount = 0 112 | script = OpReturnOut.create(data) 113 | 114 | return cls(amount, script) 115 | -------------------------------------------------------------------------------- /bitforge/transaction/transaction.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import collections 3 | 4 | from bitforge.encoding import * 5 | from bitforge.errors import * 6 | from bitforge.tools import Buffer, enforce_all, instance_of 7 | from bitforge.signature import SIGHASH_ALL 8 | from bitforge.transaction import Input, Output 9 | 10 | 11 | BaseTransaction = collections.namedtuple('Transaction', 12 | ['inputs', 'outputs', 'lock_time', 'version'] 13 | ) 14 | 15 | 16 | class Transaction(BaseTransaction): 17 | 18 | class Error(BitforgeError): 19 | pass 20 | 21 | class NoInputs(Error): 22 | "No Inputs were given to create this Transaction" 23 | 24 | class NoOutputs(Error): 25 | "No Outputs were given to create this Transaction" 26 | 27 | class NotAnInput(Error, ObjectError): 28 | "Transaction expected instances of Input, got {object} instead" 29 | 30 | class NotAnOutput(Error, ObjectError): 31 | "Transaction expected instances of Input, got {object} instead" 32 | 33 | class InvalidLockTime(Error, NumberError): 34 | "Transaction lock_time must be between 0 and 4294967295 (2^32 - 1), not {number}" 35 | 36 | 37 | def __new__(cls, inputs, outputs, lock_time = 0, version = 1): 38 | inputs = tuple(inputs) 39 | outputs = tuple(outputs) 40 | 41 | if len(inputs) == 0: raise cls.NoInputs() 42 | enforce_all(inputs, instance_of(Input), cls.NotAnInput) 43 | 44 | if len(outputs) == 0: raise cls.NoOutputs() 45 | enforce_all(outputs, instance_of(Output), cls.NotAnOutput) 46 | 47 | if not (0 <= lock_time <= 0xFFFFFFFF): 48 | raise cls.InvalidLockTime(lock_time) 49 | 50 | return super(Transaction, cls).__new__(cls, inputs, outputs, lock_time, version) 51 | 52 | def to_bytes(self): 53 | buffer = Buffer() 54 | 55 | # Version number, as little-endian uint32 (4 bytes): 56 | buffer.write(encode_int(self.version, length = 4, big_endian = False)) 57 | 58 | # Number of inputs, as variable-length integer (1-9 bytes): 59 | buffer.write(encode_varint(len(self.inputs))) 60 | 61 | # Serialized inputs (? bytes): 62 | for input in self.inputs: 63 | buffer.write(input.to_bytes()) 64 | 65 | # Number of outputs, as variable-length integer (1-9 bytes): 66 | buffer.write(encode_varint(len(self.outputs))) 67 | 68 | # Serialized outputs (? bytes): 69 | for output in self.outputs: 70 | buffer.write(output.to_bytes()) 71 | 72 | # Transaction lock time, as little-endian uint32 (4 bytes): 73 | buffer.write(encode_int(self.lock_time, length = 4, big_endian = False)) 74 | 75 | return str(buffer) 76 | 77 | def to_hex(self): 78 | return encode_hex(self.to_bytes()).decode('utf-8') 79 | 80 | def get_id_bytes(self): 81 | return sha256(sha256(self.to_bytes())) 82 | 83 | def get_id(self): 84 | return encode_hex(self.get_id_bytes()) 85 | 86 | def replace_inputs(self, inputs): 87 | return Transaction(inputs, self.outputs, self.lock_time, self.version) 88 | 89 | def sign(self, privkeys, txi_index, sigtype = SIGHASH_ALL): 90 | # A Transaction Input is signed in 4 steps: 91 | # 1. Create a simplified Transaction without data from other Inputs 92 | # 2. Sign the simplified Transaction data, discard it, keep the signature 93 | # 3. Create a new Input including the signature 94 | # 4. Build the signed Transaction, restoring data from other Inputs 95 | 96 | # Let's go step by step. 97 | 98 | # 1. Create a simplified version of the Transaction, where this Input 99 | # Script is a placeholder (the signature can't sign itself), and all 100 | # other Input scripts are empty (0 bytes). The placeholder should be 101 | # there already, manually placed or auto-created by Input subclasses. 102 | 103 | simplified_inputs = ( 104 | input.remove_script() if i != txi_index else input 105 | for i, input in enumerate(self.inputs) 106 | ) 107 | 108 | simplified_transaction = self.replace_inputs(simplified_inputs) 109 | 110 | # 2. Write the payload we're going to sign, which is the serialization 111 | # of the simplified transaction, with an extra 4 bytes for the signature 112 | # type, all of that double-sha256'd: 113 | 114 | payload = simplified_transaction.to_bytes() 115 | payload += encode_int(sigtype, length = 4, big_endian = False) 116 | payload = sha256(sha256(payload)) 117 | 118 | # 3. Create the signed Input, making it sign itself using the provided 119 | # PrivateKeys. Each Input subclass knows how to handle this process. The 120 | # signed Input will loose the placeholder Script and get a real one. 121 | 122 | signed_input = self.inputs[txi_index].sign(privkeys, payload, sigtype) 123 | 124 | # 4. Build a new Transaction, restoring the other Input Scripts, and 125 | # setting this Input to the new version including the signature: 126 | 127 | new_inputs = ( 128 | signed_input if i == txi_index else input 129 | for i, input in enumerate(self.inputs) 130 | ) 131 | 132 | return self.replace_inputs(new_inputs) # voila! 133 | 134 | 135 | @staticmethod 136 | def from_bytes(bytes): 137 | buffer = Buffer(bytes) 138 | 139 | version = decode_int(buffer.read(4), big_endian = False) 140 | 141 | ninputs = buffer.read_varint() 142 | inputs = [ Input.from_buffer(buffer) for i in range(ninputs) ] 143 | 144 | noutputs = buffer.read_varint() 145 | outputs = [ Output.from_buffer(buffer) for i in range(noutputs) ] 146 | 147 | lock_time = decode_int(buffer.read(4), big_endian = False) 148 | 149 | return Transaction(inputs, outputs, lock_time, version) 150 | 151 | @staticmethod 152 | def from_hex(string): 153 | return Transaction.from_bytes(decode_hex(string)) 154 | -------------------------------------------------------------------------------- /bitforge/unit.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | 4 | class Unit(object): 5 | 6 | btc = 1e8 7 | mbtc = 1e5 8 | bits = 1e2 9 | satoshis = 1.0 10 | 11 | @staticmethod 12 | def from_fiat(value, rate): 13 | return Unit(btc = value / float(rate)) 14 | 15 | def __init__(self, satoshis = None, bits = None, mbtc = None, btc = None): 16 | if satoshis is not None: 17 | self._set_values(satoshis) 18 | elif bits is not None: 19 | self._set_values(bits * Unit.bits) 20 | elif mbtc is not None: 21 | self._set_values(mbtc * Unit.mbtc) 22 | elif btc is not None: 23 | self._set_values(btc * Unit.btc) 24 | else: 25 | raise ValueError('Invalid arguments') 26 | 27 | def _set_values(self, satoshis): 28 | self.satoshis = int(satoshis) 29 | self.bits = self.satoshis / Unit.bits 30 | self.mbtc = self.satoshis / Unit.mbtc 31 | self.btc = self.satoshis / Unit.btc 32 | 33 | def at_rate(self, rate): 34 | return self.btc * rate 35 | 36 | def __str__(self): 37 | return '%s satoshis' % self.satoshis 38 | 39 | def __repr__(self): 40 | return '' % str(self) 41 | -------------------------------------------------------------------------------- /bitforge/uri.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | try: 4 | from urllib.parse import urlparse, parse_qsl, urlencode 5 | except ImportError: 6 | from urlparse import urlparse, parse_qsl 7 | from urllib import urlencode 8 | 9 | from bitforge import Address, Unit 10 | from bitforge.errors import BitforgeError 11 | from bitforge.compat import string_types 12 | 13 | 14 | class URI(object): 15 | 16 | @staticmethod 17 | def is_valid(uri): 18 | try: 19 | URI(uri) 20 | return True 21 | except (ValueError, BitforgeError): 22 | return False 23 | 24 | @staticmethod 25 | def parse(uri): 26 | parsed = urlparse(uri) 27 | data = dict(parse_qsl(parsed.query)) 28 | data['address'] = parsed.path or parsed.netloc 29 | return data 30 | 31 | def __init__(self, args): 32 | if isinstance(args, string_types): 33 | args = URI.parse(args) 34 | if 'amount' in args: 35 | args['amount'] = Unit(btc = float(args['amount'])).satoshis 36 | elif not isinstance(args, dict): 37 | raise ValueError('Invalid arguments') 38 | 39 | self._build_from_dict(args) 40 | 41 | def _build_from_dict(self, args): 42 | MEMBERS = ['address', 'amount', 'message', 'label', 'r'] 43 | self.extras = {} 44 | 45 | for k, v in args.items(): 46 | if k in MEMBERS: 47 | setattr(self, k, v) 48 | else: 49 | self.extras[k] = v 50 | 51 | self.address = Address.from_string(self.address) 52 | self.amount = Unit(satoshis = self.amount) if 'amount' in args else None 53 | 54 | def to_uri(self): 55 | query = self.extras.copy() 56 | if self.amount: 57 | query['amount'] = self.amount.btc 58 | for key in ['message', 'label', 'r']: 59 | value = getattr(self, key, False) 60 | if value: query[key] = value 61 | 62 | query = urlencode(sorted(query.items())) 63 | return 'bitcoin:' + self.address.to_string() + ('?' + query if query else '') 64 | 65 | def __repr__(self): 66 | return '' % self.to_uri() 67 | -------------------------------------------------------------------------------- /bitforge/utils/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from .ecdsa import is_public_pair_valid, public_pair_for_secret_exponent, public_pair_for_x, possible_public_pairs_for_signature, sign, verify 3 | 4 | from .ellipticcurve import CurveFp, Point 5 | 6 | from .secp256k1 import generator_secp256k1 7 | 8 | from . import encoding 9 | from . import intbytes 10 | -------------------------------------------------------------------------------- /bitforge/utils/ecdsa.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | """ 3 | Some portions adapted from https://github.com/warner/python-ecdsa/ Copyright (c) 2010 Brian Warner 4 | who granted its use under this license: 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the "Software"), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | 28 | Portions written in 2005 by Peter Pearson and placed in the public domain. 29 | """ 30 | 31 | import hashlib 32 | import hmac 33 | 34 | from . import intbytes 35 | from . import ellipticcurve, numbertheory 36 | 37 | 38 | if hasattr(1, "bit_length"): 39 | bit_length = lambda v: v.bit_length() 40 | else: 41 | def bit_length(self): 42 | # Make this library compatible with python < 2.7 43 | # https://docs.python.org/3.5/library/stdtypes.html#int.bit_length 44 | s = bin(self) # binary representation: bin(-37) --> '-0b100101' 45 | s = s.lstrip('-0b') # remove leading zeros and minus sign 46 | return len(s) # len('100101') --> 6 47 | 48 | 49 | def deterministic_generate_k(generator_order, secret_exponent, val, hash_f=hashlib.sha256): 50 | """ 51 | Generate K value according to https://tools.ietf.org/html/rfc6979 52 | """ 53 | n = generator_order 54 | order_size = (bit_length(n) + 7) // 8 55 | hash_size = hash_f().digest_size 56 | v = b'\x01' * hash_size 57 | k = b'\x00' * hash_size 58 | priv = intbytes.to_bytes(secret_exponent, length=order_size) 59 | shift = 8 * hash_size - bit_length(n) 60 | if shift > 0: 61 | val >>= shift 62 | if val > n: 63 | val -= n 64 | h1 = intbytes.to_bytes(val, length=order_size) 65 | k = hmac.new(k, v + b'\x00' + priv + h1, hash_f).digest() 66 | v = hmac.new(k, v, hash_f).digest() 67 | k = hmac.new(k, v + b'\x01' + priv + h1, hash_f).digest() 68 | v = hmac.new(k, v, hash_f).digest() 69 | 70 | while 1: 71 | t = bytearray() 72 | 73 | while len(t) < order_size: 74 | v = hmac.new(k, v, hash_f).digest() 75 | t.extend(v) 76 | 77 | k1 = intbytes.from_bytes(bytes(t)) 78 | 79 | k1 >>= (len(t)*8 - bit_length(n)) 80 | if k1 >= 1 and k1 < n: 81 | return k1 82 | 83 | k = hmac.new(k, v + b'\x00', hash_f).digest() 84 | v = hmac.new(k, v, hash_f).digest() 85 | 86 | 87 | def sign(generator, secret_exponent, val): 88 | """Return a signature for the provided hash, using the provided 89 | random nonce. It is absolutely vital that random_k be an unpredictable 90 | number in the range [1, self.public_key.point.order()-1]. If 91 | an attacker can guess random_k, he can compute our private key from a 92 | single signature. Also, if an attacker knows a few high-order 93 | bits (or a few low-order bits) of random_k, he can compute our private 94 | key from many signatures. The generation of nonces with adequate 95 | cryptographic strength is very difficult and far beyond the scope 96 | of this comment. 97 | 98 | May raise RuntimeError, in which case retrying with a new 99 | random value k is in order. 100 | """ 101 | G = generator 102 | n = G.order() 103 | k = deterministic_generate_k(n, secret_exponent, val) 104 | p1 = k * G 105 | r = p1.x() 106 | if r == 0: raise RuntimeError("amazingly unlucky random number r") 107 | s = ( numbertheory.inverse_mod( k, n ) * \ 108 | ( val + ( secret_exponent * r ) % n ) ) % n 109 | if s == 0: raise RuntimeError("amazingly unlucky random number s") 110 | return (r, s) 111 | 112 | def public_pair_for_secret_exponent(generator, secret_exponent): 113 | return (generator*secret_exponent).pair() 114 | 115 | def public_pair_for_x(generator, x, is_even): 116 | curve = generator.curve() 117 | p = curve.p() 118 | alpha = ( pow(x, 3, p) + curve.a() * x + curve.b() ) % p 119 | beta = numbertheory.modular_sqrt(alpha, p) 120 | if is_even == bool(beta & 1): 121 | return (x, p - beta) 122 | return (x, beta) 123 | 124 | def is_public_pair_valid(generator, public_pair): 125 | return generator.curve().contains_point(public_pair[0], public_pair[1]) 126 | 127 | def verify(generator, public_pair, val, signature): 128 | """ 129 | Verify that signature is a valid signature of hash. 130 | Return True if the signature is valid. 131 | """ 132 | 133 | # From X9.62 J.3.1. 134 | 135 | G = generator 136 | n = G.order() 137 | r, s = signature 138 | if r < 1 or r > n-1: return False 139 | if s < 1 or s > n-1: return False 140 | c = numbertheory.inverse_mod( s, n ) 141 | u1 = ( val * c ) % n 142 | u2 = ( r * c ) % n 143 | point = u1 * G + u2 * ellipticcurve.Point( G.curve(), public_pair[0], public_pair[1], G.order() ) 144 | v = point.x() % n 145 | return v == r 146 | 147 | def possible_public_pairs_for_signature(generator, value, signature): 148 | """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """ 149 | G = generator 150 | curve = G.curve() 151 | order = G.order() 152 | p = curve.p() 153 | 154 | r,s = signature 155 | 156 | possible_points = set() 157 | 158 | #recid = nV - 27 159 | # 1.1 160 | inv_r = numbertheory.inverse_mod(r,order) 161 | minus_e = -value % order 162 | x = r 163 | # 1.3 164 | alpha = ( pow(x,3,p) + curve.a() * x + curve.b() ) % p 165 | beta = numbertheory.modular_sqrt(alpha, p) 166 | for y in [beta, p - beta]: 167 | # 1.4 the constructor checks that nR is at infinity 168 | R = ellipticcurve.Point(curve, x, y, order) 169 | # 1.6 compute Q = r^-1 (sR - eG) 170 | Q = inv_r * ( s * R + minus_e * G ) 171 | public_pair = (Q.x(), Q.y()) 172 | # check that Q is the public key 173 | if verify(generator, public_pair, value, signature): 174 | # check that we get the original signing address 175 | possible_points.add(public_pair) 176 | return possible_points 177 | -------------------------------------------------------------------------------- /bitforge/utils/ellipticcurve.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | # 3 | # Implementation of elliptic curves, for cryptographic applications. 4 | # 5 | # This module doesn't provide any way to choose a random elliptic 6 | # curve, nor to verify that an elliptic curve was chosen randomly, 7 | # because one can simply use NIST's standard curves. 8 | # 9 | # Notes from X9.62-1998 (draft): 10 | # Nomenclature: 11 | # - Q is a public key. 12 | # The "Elliptic Curve Domain Parameters" include: 13 | # - q is the "field size", which in our case equals p. 14 | # - p is a big prime. 15 | # - G is a point of prime order (5.1.1.1). 16 | # - n is the order of G (5.1.1.1). 17 | # Public-key validation (5.2.2): 18 | # - Verify that Q is not the point at infinity. 19 | # - Verify that X_Q and Y_Q are in [0,p-1]. 20 | # - Verify that Q is on the curve. 21 | # - Verify that nQ is the point at infinity. 22 | # Signature generation (5.3): 23 | # - Pick random k from [1,n-1]. 24 | # Signature checking (5.4.2): 25 | # - Verify that r and s are in [1,n-1]. 26 | # 27 | # Version of 2008.11.25. 28 | # 29 | # Revision history: 30 | # 2005.12.31 - Initial version. 31 | # 2008.11.25 - Change CurveFp.is_on to contains_point. 32 | # 33 | # Written in 2005 by Peter Pearson and placed in the public domain. 34 | 35 | from . import numbertheory 36 | 37 | class NoSuchPointError(ValueError): pass 38 | 39 | 40 | class CurveFp( object ): 41 | """Elliptic Curve over the field of integers modulo a prime.""" 42 | def __init__( self, p, a, b ): 43 | """The curve of points satisfying y^2 = x^3 + a*x + b (mod p).""" 44 | self.__p = p 45 | self.__a = a 46 | self.__b = b 47 | 48 | def p( self ): 49 | return self.__p 50 | 51 | def a( self ): 52 | return self.__a 53 | 54 | def b( self ): 55 | return self.__b 56 | 57 | def contains_point( self, x, y ): 58 | """Is the point (x,y) on this curve?""" 59 | return ( y * y - ( x * x * x + self.__a * x + self.__b ) ) % self.__p == 0 60 | 61 | def __repr__(self): 62 | return '{}({!r},{!r},{!r})'.format(self.__class__.__name__, self.__p, self.__a, self.__b) 63 | 64 | def __str__(self): 65 | return 'y^2 = x^3 + {}*x + {} (mod {})'.format(self.__a, self.__b, self.__p) 66 | 67 | 68 | class Point( object ): 69 | """A point on an elliptic curve. Altering x and y is forbidden, 70 | but they can be read by the x() and y() methods.""" 71 | def __init__( self, curve, x, y, order = None ): 72 | """curve, x, y, order; order (optional) is the order of this point.""" 73 | self.__curve = curve 74 | self.__x = x 75 | self.__y = y 76 | self.__order = order 77 | # self.curve is allowed to be None only for INFINITY: 78 | if self.__curve and not self.__curve.contains_point( x, y ): 79 | raise NoSuchPointError('({},{}) is not on the curve {}'.format(x, y, curve)) 80 | if order: assert self * order == INFINITY 81 | 82 | def __eq__( self, other ): 83 | """Return 1 if the points are identical, 0 otherwise.""" 84 | if self.__curve == other.__curve \ 85 | and self.__x == other.__x \ 86 | and self.__y == other.__y: 87 | return 1 88 | else: 89 | return 0 90 | 91 | def __add__( self, other ): 92 | """Add one point to another point.""" 93 | 94 | # X9.62 B.3: 95 | 96 | if other == INFINITY: return self 97 | if self == INFINITY: return other 98 | assert self.__curve == other.__curve 99 | if self.__x == other.__x: 100 | if ( self.__y + other.__y ) % self.__curve.p() == 0: 101 | return INFINITY 102 | else: 103 | return self.double() 104 | 105 | p = self.__curve.p() 106 | 107 | l = ( ( other.__y - self.__y ) * \ 108 | numbertheory.inverse_mod( other.__x - self.__x, p ) ) % p 109 | 110 | x3 = ( l * l - self.__x - other.__x ) % p 111 | y3 = ( l * ( self.__x - x3 ) - self.__y ) % p 112 | 113 | return Point( self.__curve, x3, y3 ) 114 | 115 | def __mul__( self, other ): 116 | """Multiply a point by an integer.""" 117 | 118 | def leftmost_bit( x ): 119 | assert x > 0 120 | result = 1 121 | while result <= x: result = 2 * result 122 | return result // 2 123 | 124 | e = other 125 | if self.__order: e = e % self.__order 126 | if e == 0: return INFINITY 127 | if self == INFINITY: return INFINITY 128 | assert e > 0 129 | 130 | # From X9.62 D.3.2: 131 | 132 | e3 = 3 * e 133 | negative_self = Point( self.__curve, self.__x, -self.__y, self.__order ) 134 | i = leftmost_bit( e3 ) // 2 135 | result = self 136 | # print "Multiplying %s by %d (e3 = %d):" % ( self, other, e3 ) 137 | while i > 1: 138 | result = result.double() 139 | if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self 140 | if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self 141 | # print ". . . i = %d, result = %s" % ( i, result ) 142 | i = i // 2 143 | 144 | return result 145 | 146 | def __rmul__( self, other ): 147 | """Multiply a point by an integer.""" 148 | 149 | return self * other 150 | 151 | def __repr__( self ): 152 | return "{}({!r},{!r},{!r},{!r})".format(self.__class__.__name__, self.__curve, self.__x, self.__y, self.__order) 153 | 154 | def __str__( self ): 155 | if self == INFINITY: return "infinity" 156 | return "(%d,%d)" % ( self.__x, self.__y ) 157 | 158 | def double( self ): 159 | """Return a new point that is twice the old.""" 160 | 161 | if self == INFINITY: 162 | return INFINITY 163 | 164 | # X9.62 B.3: 165 | 166 | p = self.__curve.p() 167 | a = self.__curve.a() 168 | 169 | l = ( ( 3 * self.__x * self.__x + a ) * \ 170 | numbertheory.inverse_mod( 2 * self.__y, p ) ) % p 171 | 172 | x3 = ( l * l - 2 * self.__x ) % p 173 | y3 = ( l * ( self.__x - x3 ) - self.__y ) % p 174 | 175 | return Point( self.__curve, x3, y3 ) 176 | 177 | def x( self ): 178 | return self.__x 179 | 180 | def y( self ): 181 | return self.__y 182 | 183 | def pair( self ): 184 | return (self.__x, self.__y) 185 | 186 | def curve( self ): 187 | return self.__curve 188 | 189 | def order( self ): 190 | return self.__order 191 | 192 | 193 | # This one point is the Point At Infinity for all purposes: 194 | INFINITY = Point( None, None, None ) 195 | 196 | def __main__(): 197 | 198 | class FailedTest(Exception): pass 199 | def test_add( c, x1, y1, x2, y2, x3, y3 ): 200 | """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" 201 | p1 = Point( c, x1, y1 ) 202 | p2 = Point( c, x2, y2 ) 203 | p3 = p1 + p2 204 | print("%s + %s = %s" % ( p1, p2, p3 )) 205 | if p3.x() != x3 or p3.y() != y3: 206 | raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) 207 | else: 208 | print(" Good.") 209 | 210 | def test_double( c, x1, y1, x3, y3 ): 211 | """We expect that on curve c, 2*(x1,y1) = (x3, y3).""" 212 | p1 = Point( c, x1, y1 ) 213 | p3 = p1.double() 214 | print("%s doubled = %s" % ( p1, p3 )) 215 | if p3.x() != x3 or p3.y() != y3: 216 | raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) 217 | else: 218 | print(" Good.") 219 | 220 | def test_double_infinity( c ): 221 | """We expect that on curve c, 2*INFINITY = INFINITY.""" 222 | p1 = INFINITY 223 | p3 = p1.double() 224 | print("%s doubled = %s" % ( p1, p3 )) 225 | if p3.x() != INFINITY.x() or p3.y() != INFINITY.y(): 226 | raise FailedTest("Failure: should give (%d,%d)." % ( INFINITY.x(), INFINITY.y() )) 227 | else: 228 | print(" Good.") 229 | 230 | def test_multiply( c, x1, y1, m, x3, y3 ): 231 | """We expect that on curve c, m*(x1,y1) = (x3,y3).""" 232 | p1 = Point( c, x1, y1 ) 233 | p3 = p1 * m 234 | print("%s * %d = %s" % ( p1, m, p3 )) 235 | if p3.x() != x3 or p3.y() != y3: 236 | raise FailedTest("Failure: should give (%d,%d)." % ( x3, y3 )) 237 | else: 238 | print(" Good.") 239 | 240 | 241 | # A few tests from X9.62 B.3: 242 | 243 | c = CurveFp( 23, 1, 1 ) 244 | test_add( c, 3, 10, 9, 7, 17, 20 ) 245 | test_double( c, 3, 10, 7, 12 ) 246 | test_add( c, 3, 10, 3, 10, 7, 12 ) # (Should just invoke double.) 247 | test_multiply( c, 3, 10, 2, 7, 12 ) 248 | 249 | test_double_infinity(c) 250 | 251 | # From X9.62 I.1 (p. 96): 252 | 253 | g = Point( c, 13, 7, 7 ) 254 | 255 | check = INFINITY 256 | for i in range( 7 + 1 ): 257 | p = ( i % 7 ) * g 258 | print("%s * %d = %s, expected %s . . ." % ( g, i, p, check )) 259 | if p == check: 260 | print(" Good.") 261 | else: 262 | raise FailedTest("Bad.") 263 | check = check + g 264 | 265 | # NIST Curve P-192: 266 | p = 6277101735386680763835789423207666416083908700390324961279 267 | r = 6277101735386680763835789423176059013767194773182842284081 268 | #s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L 269 | c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 270 | b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 271 | Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 272 | Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 273 | 274 | c192 = CurveFp( p, -3, b ) 275 | p192 = Point( c192, Gx, Gy, r ) 276 | 277 | # Checking against some sample computations presented 278 | # in X9.62: 279 | 280 | d = 651056770906015076056810763456358567190100156695615665659 281 | Q = d * p192 282 | if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5: 283 | raise FailedTest("p192 * d came out wrong.") 284 | else: 285 | print("p192 * d came out right.") 286 | 287 | k = 6140507067065001063065065565667405560006161556565665656654 288 | R = k * p192 289 | if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ 290 | or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: 291 | raise FailedTest("k * p192 came out wrong.") 292 | else: 293 | print("k * p192 came out right.") 294 | 295 | u1 = 2563697409189434185194736134579731015366492496392189760599 296 | u2 = 6266643813348617967186477710235785849136406323338782220568 297 | temp = u1 * p192 + u2 * Q 298 | if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ 299 | or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: 300 | raise FailedTest("u1 * p192 + u2 * Q came out wrong.") 301 | else: 302 | print("u1 * p192 + u2 * Q came out right.") 303 | 304 | if __name__ == "__main__": 305 | __main__() 306 | -------------------------------------------------------------------------------- /bitforge/utils/encoding.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | """ 3 | Various utilities useful for converting one Bitcoin format to another, including some 4 | the human-transcribable format hashed_base58. 5 | 6 | 7 | The MIT License (MIT) 8 | 9 | Copyright (c) 2013 by Richard Kiss 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | """ 29 | 30 | import hashlib 31 | 32 | from .intbytes import byte_to_int, bytes_from_int 33 | 34 | 35 | BASE58_ALPHABET = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 36 | BASE58_BASE = len(BASE58_ALPHABET) 37 | BASE58_LOOKUP = dict((c, i) for i, c in enumerate(BASE58_ALPHABET)) 38 | 39 | 40 | class EncodingError(Exception): 41 | pass 42 | 43 | 44 | def ripemd160(data): 45 | return hashlib.new("ripemd160", data) 46 | 47 | try: 48 | ripemd160(b'').digest() 49 | except ValueError: 50 | # stupid Google App Engine hashlib doesn't support ripemd160 for some stupid reason 51 | # import it from pycrypto. You need to add 52 | # - name: pycrypto 53 | # version: "latest" 54 | # to the "libraries" section of your app.yaml 55 | from Crypto.Hash.RIPEMD import RIPEMD160Hash as ripemd160 56 | 57 | 58 | def to_long(base, lookup_f, s): 59 | """ 60 | Convert an array to a (possibly bignum) integer, along with a prefix value 61 | of how many prefixed zeros there are. 62 | 63 | base: 64 | the source base 65 | lookup_f: 66 | a function to convert an element of s to a value between 0 and base-1. 67 | s: 68 | the value to convert 69 | """ 70 | prefix = 0 71 | v = 0 72 | for c in s: 73 | v *= base 74 | try: 75 | v += lookup_f(c) 76 | except Exception: 77 | raise EncodingError("bad character %s in string %s" % (c, s)) 78 | if v == 0: 79 | prefix += 1 80 | return v, prefix 81 | 82 | 83 | def from_long(v, prefix, base, charset): 84 | """The inverse of to_long. Convert an integer to an arbitrary base. 85 | 86 | v: the integer value to convert 87 | prefix: the number of prefixed 0s to include 88 | base: the new base 89 | charset: an array indicating what printable character to use for each value. 90 | """ 91 | l = bytearray() 92 | while v > 0: 93 | try: 94 | v, mod = divmod(v, base) 95 | l.append(charset(mod)) 96 | except Exception: 97 | raise EncodingError("can't convert to character corresponding to %d" % mod) 98 | l.extend([charset(0)] * prefix) 99 | l.reverse() 100 | return bytes(l) 101 | 102 | 103 | def to_bytes_32(v): 104 | v = from_long(v, 0, 256, lambda x: x) 105 | if len(v) > 32: 106 | raise ValueError("input to to_bytes_32 is too large") 107 | return ((b'\0' * 32) + v)[-32:] 108 | 109 | if hasattr(int, "to_bytes"): 110 | to_bytes_32 = lambda v: v.to_bytes(32, byteorder="big") 111 | 112 | 113 | def from_bytes_32(v): 114 | if len(v) != 32: 115 | raise ValueError("input to from_bytes_32 is wrong length") 116 | return to_long(256, byte_to_int, v)[0] 117 | 118 | if hasattr(int, "from_bytes"): 119 | from_bytes_32 = lambda v: int.from_bytes(v, byteorder="big") 120 | 121 | 122 | def double_sha256(data): 123 | """A standard compound hash.""" 124 | return hashlib.sha256(hashlib.sha256(data).digest()).digest() 125 | 126 | 127 | def hash160(data): 128 | """A standard compound hash.""" 129 | return ripemd160(hashlib.sha256(data).digest()).digest() 130 | 131 | 132 | def b2a_base58(s): 133 | """Convert binary to base58 using BASE58_ALPHABET. Like Bitcoin addresses.""" 134 | v, prefix = to_long(256, byte_to_int, s) 135 | s = from_long(v, prefix, BASE58_BASE, lambda v: BASE58_ALPHABET[v]) 136 | return s.decode("utf8") 137 | 138 | 139 | def a2b_base58(s): 140 | """Convert base58 to binary using BASE58_ALPHABET.""" 141 | v, prefix = to_long(BASE58_BASE, lambda c: BASE58_LOOKUP[c], s.encode("utf8")) 142 | return from_long(v, prefix, 256, lambda x: x) 143 | 144 | 145 | def b2a_hashed_base58(data): 146 | """ 147 | A "hashed_base58" structure is a base58 integer (which looks like a string) 148 | with four bytes of hash data at the end. Bitcoin does this in several places, 149 | including Bitcoin addresses. 150 | 151 | This function turns data (of type "bytes") into its hashed_base58 equivalent. 152 | """ 153 | return b2a_base58(data + double_sha256(data)[:4]) 154 | 155 | 156 | def a2b_hashed_base58(s): 157 | """ 158 | If the passed string is hashed_base58, return the binary data. 159 | Otherwise raises an EncodingError. 160 | """ 161 | data = a2b_base58(s) 162 | data, the_hash = data[:-4], data[-4:] 163 | if double_sha256(data)[:4] == the_hash: 164 | return data 165 | raise EncodingError("hashed base58 has bad checksum %s" % s) 166 | 167 | 168 | def is_hashed_base58_valid(base58): 169 | """Return True if and only if base58 is valid hashed_base58.""" 170 | try: 171 | a2b_hashed_base58(base58) 172 | except EncodingError: 173 | return False 174 | return True 175 | 176 | 177 | def wif_to_tuple_of_prefix_secret_exponent_compressed(wif): 178 | """ 179 | Return a tuple of (prefix, secret_exponent, is_compressed). 180 | """ 181 | decoded = a2b_hashed_base58(wif) 182 | actual_prefix, private_key = decoded[:1], decoded[1:] 183 | compressed = len(private_key) > 32 184 | return actual_prefix, from_bytes_32(private_key[:32]), compressed 185 | 186 | 187 | def wif_to_tuple_of_secret_exponent_compressed(wif, allowable_wif_prefixes=[b'\x80']): 188 | """Convert a WIF string to the corresponding secret exponent. Private key manipulation. 189 | Returns a tuple: the secret exponent, as a bignum integer, and a boolean indicating if the 190 | WIF corresponded to a compressed key or not. 191 | 192 | Not that it matters, since we can use the secret exponent to generate both the compressed 193 | and uncompressed Bitcoin address.""" 194 | actual_prefix, secret_exponent, is_compressed = wif_to_tuple_of_prefix_secret_exponent_compressed(wif) 195 | if actual_prefix not in allowable_wif_prefixes: 196 | raise EncodingError("unexpected first byte of WIF %s" % wif) 197 | return secret_exponent, is_compressed 198 | 199 | 200 | def wif_to_secret_exponent(wif, allowable_wif_prefixes=[b'\x80']): 201 | """Convert a WIF string to the corresponding secret exponent.""" 202 | return wif_to_tuple_of_secret_exponent_compressed(wif, allowable_wif_prefixes=allowable_wif_prefixes)[0] 203 | 204 | 205 | def is_valid_wif(wif, allowable_wif_prefixes=[b'\x80']): 206 | """Return a boolean indicating if the WIF is valid.""" 207 | try: 208 | wif_to_secret_exponent(wif, allowable_wif_prefixes=allowable_wif_prefixes) 209 | except EncodingError: 210 | return False 211 | return True 212 | 213 | 214 | def secret_exponent_to_wif(secret_exp, compressed=True, wif_prefix=b'\x80'): 215 | """Convert a secret exponent (correspdong to a private key) to WIF format.""" 216 | d = wif_prefix + to_bytes_32(secret_exp) 217 | if compressed: 218 | d += b'\01' 219 | return b2a_hashed_base58(d) 220 | 221 | 222 | def public_pair_to_sec(public_pair, compressed=True): 223 | """Convert a public pair (a pair of bignums corresponding to a public key) to the 224 | gross internal sec binary format used by OpenSSL.""" 225 | x_str = to_bytes_32(public_pair[0]) 226 | if compressed: 227 | return bytes_from_int((2 + (public_pair[1] & 1))) + x_str 228 | y_str = to_bytes_32(public_pair[1]) 229 | return b'\4' + x_str + y_str 230 | 231 | 232 | def sec_to_public_pair(sec): 233 | """Convert a public key in sec binary format to a public pair.""" 234 | x = from_bytes_32(sec[1:33]) 235 | sec0 = sec[:1] 236 | if sec0 == b'\4': 237 | y = from_bytes_32(sec[33:65]) 238 | from .ecdsa import is_public_pair_valid 239 | from .secp256k1 import generator_secp256k1 240 | public_pair = (x, y) 241 | # verify this is on the curve 242 | if not is_public_pair_valid(generator_secp256k1, public_pair): 243 | raise EncodingError("invalid (x, y) pair") 244 | return public_pair 245 | if sec0 in (b'\2', b'\3'): 246 | from .ecdsa import public_pair_for_x 247 | from .secp256k1 import generator_secp256k1 248 | return public_pair_for_x(generator_secp256k1, x, is_even=(sec0 == b'\2')) 249 | raise EncodingError("bad sec encoding for public key") 250 | 251 | 252 | def is_sec_compressed(sec): 253 | """Return a boolean indicating if the sec represents a compressed public key.""" 254 | return sec[:1] in (b'\2', b'\3') 255 | 256 | 257 | def public_pair_to_hash160_sec(public_pair, compressed=True): 258 | """Convert a public_pair (corresponding to a public key) to hash160_sec format. 259 | This is a hash of the sec representation of a public key, and is used to generate 260 | the corresponding Bitcoin address.""" 261 | return hash160(public_pair_to_sec(public_pair, compressed=compressed)) 262 | 263 | 264 | def hash160_sec_to_bitcoin_address(hash160_sec, address_prefix=b'\0'): 265 | """Convert the hash160 of a sec version of a public_pair to a Bitcoin address.""" 266 | return b2a_hashed_base58(address_prefix + hash160_sec) 267 | 268 | 269 | def bitcoin_address_to_hash160_sec_with_prefix(bitcoin_address): 270 | """ 271 | Convert a Bitcoin address back to the hash160_sec format and 272 | also return the prefix. 273 | """ 274 | blob = a2b_hashed_base58(bitcoin_address) 275 | if len(blob) != 21: 276 | raise EncodingError("incorrect binary length (%d) for Bitcoin address %s" % 277 | (len(blob), bitcoin_address)) 278 | if blob[:1] not in [b'\x6f', b'\0']: 279 | raise EncodingError("incorrect first byte (%s) for Bitcoin address %s" % (blob[0], bitcoin_address)) 280 | return blob[1:], blob[:1] 281 | 282 | 283 | def bitcoin_address_to_hash160_sec(bitcoin_address, address_prefix=b'\0'): 284 | """Convert a Bitcoin address back to the hash160_sec format of the public key. 285 | Since we only know the hash of the public key, we can't get the full public key back.""" 286 | hash160, actual_prefix = bitcoin_address_to_hash160_sec_with_prefix(bitcoin_address) 287 | if (address_prefix == actual_prefix): 288 | return hash160 289 | raise EncodingError("Bitcoin address %s for wrong network" % bitcoin_address) 290 | 291 | 292 | def public_pair_to_bitcoin_address(public_pair, compressed=True, address_prefix=b'\0'): 293 | """Convert a public_pair (corresponding to a public key) to a Bitcoin address.""" 294 | return hash160_sec_to_bitcoin_address(public_pair_to_hash160_sec( 295 | public_pair, compressed=compressed), address_prefix=address_prefix) 296 | 297 | 298 | def is_valid_bitcoin_address(bitcoin_address, allowable_prefixes=b'\0'): 299 | """Return True if and only if bitcoin_address is valid.""" 300 | try: 301 | hash160, prefix = bitcoin_address_to_hash160_sec_with_prefix(bitcoin_address) 302 | except EncodingError: 303 | return False 304 | return prefix in allowable_prefixes 305 | -------------------------------------------------------------------------------- /bitforge/utils/intbytes.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | """ 3 | Provide the following functions: 4 | 5 | bytes_to_ints(bytes): 6 | yield an iterator of ints. Designed to deal with how 7 | Python 2 treats bytes[0] as a string while 8 | Python 3 treats bytes[0] as an int. 9 | 10 | bytes_from_int(an_int): 11 | convert a small integer (< 256) into bytes (of length 1) 12 | 13 | byte_to_int(one_byte): 14 | turn one byte into an int 15 | 16 | bytes_from_ints(list_of_small_ints): 17 | return a bytes object from a list of small (< 256) integers 18 | 19 | to_bytes(v, length, byteorder): 20 | convert integer v into a bytes object 21 | 22 | from_bytes(bytes, byteorder, *, signed=False): 23 | convert the bytes object into an integer 24 | 25 | The last two functions are designed to mimic the methods of the same 26 | name that exist on int in Python 3 only. For Python 3, it uses 27 | those implementations. 28 | """ 29 | 30 | bytes_to_ints = (lambda x: [ord(c) for c in x]) if bytes == str else lambda x: x 31 | bytes_from_int = chr if bytes == str else lambda x: bytes([x]) 32 | byte_to_int = ord if bytes == str else lambda x: x 33 | bytes_from_ints = (lambda l: b''.join(chr(x) for x in l)) if bytes == str else bytes 34 | 35 | 36 | if hasattr(int, "to_bytes"): 37 | to_bytes = lambda v, length, byteorder="big": v.to_bytes(length, byteorder=byteorder) 38 | from_bytes = lambda bytes, byteorder="big", signed=False: int.from_bytes( 39 | bytes, byteorder="big", signed=signed) 40 | int_to_bytes = lambda v: v.to_bytes((v.bit_length()+7)//8, byteorder="big") 41 | int_from_bytes = lambda v: int.from_bytes(v, byteorder="big") 42 | else: 43 | def to_bytes(v, length, byteorder="big"): 44 | l = bytearray() 45 | for i in range(length): 46 | mod = v & 0xff 47 | v >>= 8 48 | l.append(mod) 49 | if byteorder == "big": 50 | l.reverse() 51 | return bytes(l) 52 | 53 | def from_bytes(bytes, byteorder="big", signed=False): 54 | if byteorder != "big": 55 | bytes = reversed(bytes) 56 | v = 0 57 | for c in bytes_to_ints(bytes): 58 | v <<= 8 59 | v += c 60 | if signed and bytes[0] & 0x80: 61 | v = v - (1 << (8*len(bytes))) 62 | return v 63 | 64 | def int_to_bytes(v): 65 | l = bytearray() 66 | while v > 0: 67 | l.append(v & 0xff) 68 | v >>= 8 69 | l.reverse() 70 | return bytes(l) 71 | 72 | def int_from_bytes(s): 73 | v = 0 74 | for c in bytes_to_ints(s): 75 | v <<= 8 76 | v += c 77 | return v 78 | -------------------------------------------------------------------------------- /bitforge/utils/numbertheory.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | def inverse_mod( a, m ): 4 | """Inverse of a mod m.""" 5 | 6 | if a < 0 or m <= a: a = a % m 7 | 8 | # From Ferguson and Schneier, roughly: 9 | 10 | c, d = a, m 11 | uc, vc, ud, vd = 1, 0, 0, 1 12 | while c != 0: 13 | q, c, d = divmod( d, c ) + ( c, ) 14 | uc, vc, ud, vd = ud - q*uc, vd - q*vc, uc, vc 15 | 16 | # At this point, d is the GCD, and ud*a+vd*m = d. 17 | # If d == 1, this means that ud is a inverse. 18 | 19 | assert d == 1 20 | if ud > 0: return ud 21 | else: return ud + m 22 | 23 | # from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/ 24 | 25 | def modular_sqrt(a, p): 26 | """ Find a quadratic residue (mod p) of 'a'. p 27 | must be an odd prime. 28 | 29 | Solve the congruence of the form: 30 | x^2 = a (mod p) 31 | And returns x. Note that p - x is also a root. 32 | 33 | 0 is returned is no square root exists for 34 | these a and p. 35 | 36 | The Tonelli-Shanks algorithm is used (except 37 | for some simple cases in which the solution 38 | is known from an identity). This algorithm 39 | runs in polynomial time (unless the 40 | generalized Riemann hypothesis is false). 41 | """ 42 | # Simple cases 43 | # 44 | if legendre_symbol(a, p) != 1: 45 | return 0 46 | elif a == 0: 47 | return 0 48 | elif p == 2: 49 | return p 50 | elif p % 4 == 3: 51 | return pow(a, (p + 1) // 4, p) 52 | 53 | # Partition p-1 to s * 2^e for an odd s (i.e. 54 | # reduce all the powers of 2 from p-1) 55 | # 56 | s = p - 1 57 | e = 0 58 | while s % 2 == 0: 59 | s /= 2 60 | e += 1 61 | 62 | # Find some 'n' with a legendre symbol n|p = -1. 63 | # Shouldn't take long. 64 | # 65 | n = 2 66 | while legendre_symbol(n, p) != -1: 67 | n += 1 68 | 69 | # Here be dragons! 70 | # Read the paper "Square roots from 1; 24, 51, 71 | # 10 to Dan Shanks" by Ezra Brown for more 72 | # information 73 | # 74 | 75 | # x is a guess of the square root that gets better 76 | # with each iteration. 77 | # b is the "fudge factor" - by how much we're off 78 | # with the guess. The invariant x^2 = ab (mod p) 79 | # is maintained throughout the loop. 80 | # g is used for successive powers of n to update 81 | # both a and b 82 | # r is the exponent - decreases with each update 83 | # 84 | x = pow(a, (s + 1) // 2, p) 85 | b = pow(a, s, p) 86 | g = pow(n, s, p) 87 | r = e 88 | 89 | while True: 90 | t = b 91 | m = 0 92 | for m in range(r): 93 | if t == 1: 94 | break 95 | t = pow(t, 2, p) 96 | 97 | if m == 0: 98 | return x 99 | 100 | gs = pow(g, 2 ** (r - m - 1), p) 101 | g = (gs * gs) % p 102 | x = (x * gs) % p 103 | b = (b * g) % p 104 | r = m 105 | 106 | def legendre_symbol(a, p): 107 | """ Compute the Legendre symbol a|p using 108 | Euler's criterion. p is a prime, a is 109 | relatively prime to p (if p divides 110 | a, then a|p = 0) 111 | 112 | Returns 1 if a has a square root modulo 113 | p, -1 otherwise. 114 | """ 115 | ls = pow(a, (p - 1) // 2, p) 116 | return -1 if ls == p - 1 else ls 117 | -------------------------------------------------------------------------------- /bitforge/utils/secp256k1.py: -------------------------------------------------------------------------------- 1 | from .ellipticcurve import CurveFp, Point 2 | 3 | # Certicom secp256-k1 4 | _a = 0x0000000000000000000000000000000000000000000000000000000000000000 5 | _b = 0x0000000000000000000000000000000000000000000000000000000000000007 6 | _p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f 7 | _Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 8 | _Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 9 | _r = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 10 | 11 | generator_secp256k1 = Point( CurveFp( _p, _a, _b ), _Gx, _Gy, _r ) 12 | -------------------------------------------------------------------------------- /examples/example_pubkey.py: -------------------------------------------------------------------------------- 1 | from example_utils import log 2 | 3 | from bitforge import networks 4 | from bitforge import AddressInput, AddressOutput, Transaction, PrivateKey 5 | 6 | 7 | PK_HEX = '21c601c0ae6dfcdcf622e6fe2be9153ed7ada0cc90a8a08475e57060e18c0791' 8 | 9 | PREV_TX_ID = 'da5360df3b0d4857cc1b785929dff052380c53f73017f258719de667f77e7dca' 10 | UTXO_INDEX = 0 11 | AMOUNT = 11000 12 | 13 | 14 | privkey = PrivateKey.from_hex(PK_HEX, network = networks.testnet) 15 | pubkey = privkey.to_public_key() 16 | address = pubkey.to_address() 17 | 18 | 19 | inputs = [ AddressInput.create(PREV_TX_ID, UTXO_INDEX, address) ] 20 | outputs = [ AddressOutput.create(AMOUNT - 1000, address) ] 21 | 22 | tx = Transaction(inputs, outputs) 23 | 24 | signed_transaction_hex = tx.sign([ privkey ], 0).to_hex() 25 | 26 | 27 | print('') 28 | log("Private Key", privkey.to_hex()) 29 | log("Public Key", pubkey.to_hex()) 30 | log("Address", address.to_string()) 31 | log("Signed transaction hex", signed_transaction_hex) 32 | -------------------------------------------------------------------------------- /examples/example_serial.py: -------------------------------------------------------------------------------- 1 | from bitforge.encoding import * 2 | from bitforge import Input, Output, AddressInput, AddressOutput, Transaction, PrivateKey 3 | from bitforge import networks 4 | 5 | import termcolor # not a library dependency, obviously 6 | 7 | 8 | def log(title, content): 9 | print(termcolor.colored(title + ":", 'green', attrs = ['bold'])) 10 | print(content) 11 | print('') 12 | 13 | 14 | PK_HEX = '21c601c0ae6dfcdcf622e6fe2be9153ed7ada0cc90a8a08475e57060e18c0791' 15 | 16 | PREV_TX_ID = '4baa7551933fbf26158a619c3084ccdd5c0d81930b3e74a85a33ad26d13f1a55' 17 | UTXO_INDEX = 0 18 | AMOUNT = 2000 19 | 20 | 21 | privkey = PrivateKey.from_hex(PK_HEX, network = networks.testnet) 22 | pubkey = privkey.to_public_key() 23 | address = pubkey.to_address() 24 | 25 | i = AddressInput.create(PREV_TX_ID, UTXO_INDEX, address) 26 | o = AddressOutput.create(AMOUNT, address) 27 | 28 | t1 = Transaction([i], [o]) 29 | t2 = Transaction.from_bytes(t1.to_bytes()) 30 | 31 | log("Transaction hex 1", t1.to_hex()) 32 | log("Transaction hex 2", t2.to_hex()) 33 | print('equal', t1 == t2) 34 | # __import__('IPython').embed() 35 | -------------------------------------------------------------------------------- /examples/example_utils.py: -------------------------------------------------------------------------------- 1 | import termcolor 2 | 3 | def log(title, content): 4 | print(termcolor.colored(title + ":", 'green', attrs = ['bold'])) 5 | print(content) 6 | print('') 7 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = tests 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | enum34==1.1.2 2 | ecdsa==0.13 3 | pytest==2.9.0 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test = pytest 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | import sys 3 | 4 | setuptools.setup( 5 | name = 'bitforge', 6 | version = '0.3', 7 | url = 'https://github.com/coinforge/bitforge', 8 | 9 | author = 'Yemel Jardi', 10 | author_email = 'angel.jardi@gmail.com', 11 | 12 | description = 'A python bitcoin library', 13 | keywords = ['bitcoin', 'altcoin'], # arbitrary keywords 14 | 15 | packages = setuptools.find_packages(), 16 | 17 | setup_requires=['pytest-runner'], 18 | tests_require=['pytest'], 19 | install_requires = ['ecdsa==0.13'] + (['enum34==1.0.4'] if sys.version_info < (3, 4) else []), 20 | 21 | classifiers = [ 22 | 'Development Status :: 4 - Beta', 23 | 'Intended Audience :: Developers', 24 | 'License :: OSI Approved :: MIT License', 25 | 'Operating System :: OS Independent', 26 | 'Programming Language :: Python', 27 | 'Programming Language :: Python :: 2', 28 | 'Programming Language :: Python :: 2.7', 29 | 'Programming Language :: Python :: 3', 30 | 'Programming Language :: Python :: 3.3', 31 | 'Programming Language :: Python :: 3.4', 32 | 'Programming Language :: Python :: 3.5', 33 | 'Topic :: Internet', 34 | 'Topic :: Software Development :: Libraries :: Python Modules', 35 | ], 36 | ) 37 | -------------------------------------------------------------------------------- /tests/data/invalid_wifs.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "" 4 | ], 5 | [ 6 | "x" 7 | ], 8 | [ 9 | "37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y" 10 | ], 11 | [ 12 | "dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv" 13 | ], 14 | [ 15 | "MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S" 16 | ], 17 | [ 18 | "rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf" 19 | ], 20 | [ 21 | "4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq" 22 | ], 23 | [ 24 | "7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb" 25 | ], 26 | [ 27 | "17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs" 28 | ], 29 | [ 30 | "KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3" 31 | ], 32 | [ 33 | "7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th" 34 | ], 35 | [ 36 | "cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va" 37 | ], 38 | [ 39 | "gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk" 40 | ], 41 | [ 42 | "emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs" 43 | ], 44 | [ 45 | "7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo" 46 | ], 47 | [ 48 | "1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso" 49 | ], 50 | [ 51 | "31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq" 52 | ], 53 | [ 54 | "DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN" 55 | ], 56 | [ 57 | "2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i" 58 | ], 59 | [ 60 | "7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos" 61 | ], 62 | [ 63 | "1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu" 64 | ], 65 | [ 66 | "2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb" 67 | ], 68 | [ 69 | "8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ" 70 | ], 71 | [ 72 | "163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ" 73 | ], 74 | [ 75 | "2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu" 76 | ], 77 | [ 78 | "461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU" 79 | ], 80 | [ 81 | "2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs" 82 | ], 83 | [ 84 | "cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn" 85 | ], 86 | [ 87 | "gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj" 88 | ], 89 | [ 90 | "nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny" 91 | ], 92 | [ 93 | "L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc" 94 | ], 95 | [ 96 | "7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ" 97 | ], 98 | [ 99 | "2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP" 100 | ], 101 | [ 102 | "dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw" 103 | ], 104 | [ 105 | "HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX" 106 | ], 107 | [ 108 | "4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB" 109 | ], 110 | [ 111 | "Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ" 112 | ], 113 | [ 114 | "Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs" 115 | ], 116 | [ 117 | "6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ" 118 | ], 119 | [ 120 | "giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4" 121 | ], 122 | [ 123 | "cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK" 124 | ], 125 | [ 126 | "37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig" 127 | ], 128 | [ 129 | "EsYbG4tWWWY45G31nox838qNdzksbPySWc" 130 | ], 131 | [ 132 | "nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT" 133 | ], 134 | [ 135 | "cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx" 136 | ], 137 | [ 138 | "1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde" 139 | ], 140 | [ 141 | "2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU" 142 | ], 143 | [ 144 | "ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf" 145 | ], 146 | [ 147 | "Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd" 148 | ], 149 | [ 150 | "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" 151 | ] 152 | ] -------------------------------------------------------------------------------- /tests/data/privkey.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinforge/bitforge/ace015c565a5d95848f874d2f8c6586309e5950c/tests/data/privkey.json -------------------------------------------------------------------------------- /tests/data/tx_invalid.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["The following are deserialized transactions which are invalid."], 3 | ["They are in the form"], 4 | ["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"], 5 | ["serializedTransaction, verifyFlags]"], 6 | ["Objects that are only a single string (like this one) are ignored"], 7 | 8 | ["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"], 9 | [[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]], 10 | "010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "P2SH"], 11 | 12 | ["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], 13 | ["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"], 14 | ["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"], 15 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], 16 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], 17 | 18 | ["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"], 19 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]], 20 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], 21 | 22 | ["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"], 23 | ["but with the signature duplicated in the scriptPubKey with a different hashtype suffix"], 24 | ["See FindAndDelete, which will only remove if the signature, including the hash type, matches"], 25 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a81"]], 26 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"], 27 | 28 | ["An invalid P2SH Transaction"], 29 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], 30 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"], 31 | 32 | ["Tests for CheckTransaction()"], 33 | ["No inputs"], 34 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], 35 | "0100000000010000000000000000015100000000", "P2SH"], 36 | 37 | ["No outputs"], 38 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], 39 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"], 40 | 41 | ["Negative output"], 42 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xae609aca8061d77c5e111f6bb62501a6bbe2bfdb EQUAL"]], 43 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d4830450220063222cbb128731fc09de0d7323746539166544d6c1df84d867ccea84bcc8903022100bf568e8552844de664cd41648a031554327aa8844af34b4f27397c65b92c04de0123210243ec37dee0e2e053a9c976f43147e79bc7d9dc606ea51010af1ac80db6b069e1acffffffff01ffffffffffffffff015100000000", "P2SH"], 44 | 45 | ["MAX_MONEY + 1 output"], 46 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]], 47 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", "P2SH"], 48 | 49 | ["MAX_MONEY output + 1 output"], 50 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]], 51 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", "P2SH"], 52 | 53 | ["Duplicate inputs"], 54 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x236d0639db62b0773fd8ac34dc85ae19e9aba80a EQUAL"]], 55 | "01000000020001000000000000000000000000000000000000000000000000000000000000000000006c47304402204bb1197053d0d7799bf1b30cd503c44b58d6240cccbdc85b6fe76d087980208f02204beeed78200178ffc6c74237bb74b3f276bbb4098b5605d814304fe128bf1431012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff0001000000000000000000000000000000000000000000000000000000000000000000006c47304402202306489afef52a6f62e90bf750bbcdf40c06f5c6b138286e6b6b86176bb9341802200dba98486ea68380f47ebb19a7df173b99e6bc9c681d6ccf3bde31465d1f16b3012321039e8815e15952a7c3fada1905f8cf55419837133bd7756c0ef14fc8dfe50c0deaacffffffff010000000000000000015100000000", "P2SH"], 56 | 57 | ["Coinbase of size 1"], 58 | ["Note the input is just required to make the tester happy"], 59 | [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], 60 | "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0151ffffffff010000000000000000015100000000", "P2SH"], 61 | 62 | ["Coinbase of size 101"], 63 | ["Note the input is just required to make the tester happy"], 64 | [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], 65 | "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff655151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "P2SH"], 66 | 67 | ["Null txin, but without being a coinbase (because there are two inputs)"], 68 | [[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"], 69 | ["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], 70 | "01000000020000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff00010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015100000000", "P2SH"], 71 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"], 72 | ["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]], 73 | "010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff010000000000000000015100000000", "P2SH"], 74 | 75 | ["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"], 76 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"], 77 | ["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]], 78 | "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", "P2SH"], 79 | 80 | ["CHECKMULTISIG with incorrect signature order"], 81 | ["Note the input is just required to make the tester happy"], 82 | [[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]], 83 | "01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"], 84 | 85 | 86 | ["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], 87 | ["It is an OP_CHECKMULTISIG with the dummy value missing"], 88 | [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], 89 | "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"], 90 | 91 | 92 | ["CHECKMULTISIG SCRIPT_VERIFY_NULLDUMMY tests:"], 93 | 94 | ["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"], 95 | ["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"], 96 | [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], 97 | "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], 98 | 99 | ["As above, but using a OP_1"], 100 | [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], 101 | "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], 102 | 103 | ["As above, but using a OP_1NEGATE"], 104 | [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], 105 | "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], 106 | 107 | ["As above, but with the dummy byte missing"], 108 | [[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]], 109 | "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"], 110 | 111 | 112 | ["Empty stack when we try to run CHECKSIG"], 113 | [[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]], 114 | "01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "P2SH"], 115 | 116 | 117 | ["Inverted versions of tx_valid CODESEPARATOR IF block tests"], 118 | 119 | ["CODESEPARATOR in an unexecuted IF block does not change what is hashed"], 120 | [[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], 121 | "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0151ffffffff010000000000000000016a00000000", "P2SH"], 122 | 123 | ["As above, with the IF block executed"], 124 | [[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], 125 | "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "P2SH"], 126 | 127 | ["CHECKLOCKTIMEVERIFY tests"], 128 | 129 | ["By-height locks, with argument just beyond tx nLockTime"], 130 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 CHECKLOCKTIMEVERIFY 1"]], 131 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 132 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]], 133 | "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000fe64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 134 | 135 | ["By-time locks, with argument just beyond tx nLockTime (but within numerical boundaries)"], 136 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000001 CHECKLOCKTIMEVERIFY 1"]], 137 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 138 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967295 CHECKLOCKTIMEVERIFY 1"]], 139 | "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000feffffff", "P2SH,CHECKLOCKTIMEVERIFY"], 140 | 141 | ["Argument missing"], 142 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "CHECKLOCKTIMEVERIFY 1"]], 143 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 144 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], 145 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000001b1010000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 146 | 147 | ["Argument negative with by-blockheight nLockTime=0"], 148 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY 1"]], 149 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 150 | 151 | ["Argument negative with by-blocktime nLockTime=500,000,000"], 152 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY 1"]], 153 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 154 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], 155 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], 156 | 157 | ["Input locked"], 158 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], 159 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 160 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], 161 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1ffffffff0100000000000000000002000000", "P2SH,CHECKLOCKTIMEVERIFY"], 162 | 163 | ["Another input being unlocked isn't sufficient; the CHECKLOCKTIMEVERIFY-using input must be unlocked"], 164 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"] , 165 | ["0000000000000000000000000000000000000000000000000000000000000200", 1, "1"]], 166 | "010000000200010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00020000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 167 | 168 | ["Argument/tx height/time mismatch, both versions"], 169 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]], 170 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 171 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0"]], 172 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b100000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 173 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "499999999 CHECKLOCKTIMEVERIFY 1"]], 174 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 175 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]], 176 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 177 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "500000000 CHECKLOCKTIMEVERIFY 1"]], 178 | "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ff64cd1d", "P2SH,CHECKLOCKTIMEVERIFY"], 179 | 180 | ["Argument 2^32 with nLockTime=2^32-1"], 181 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4294967296 CHECKLOCKTIMEVERIFY 1"]], 182 | "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffffff", "P2SH,CHECKLOCKTIMEVERIFY"], 183 | 184 | ["Same, but with nLockTime=2^31-1"], 185 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "2147483648 CHECKLOCKTIMEVERIFY 1"]], 186 | "0100000001000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000ffffff7f", "P2SH,CHECKLOCKTIMEVERIFY"], 187 | 188 | ["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], 189 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 CHECKLOCKTIMEVERIFY 1"]], 190 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 191 | 192 | ["Failure due to failing CHECKLOCKTIMEVERIFY in scriptSig"], 193 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], 194 | "01000000010001000000000000000000000000000000000000000000000000000000000000000000000251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 195 | 196 | ["Failure due to failing CHECKLOCKTIMEVERIFY in redeemScript"], 197 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xc5b93064159b3b2d6ab506a41b1f50463771b988 EQUAL"]], 198 | "0100000001000100000000000000000000000000000000000000000000000000000000000000000000030251b1000000000100000000000000000000000000", "P2SH,CHECKLOCKTIMEVERIFY"], 199 | 200 | ["A transaction with a non-standard DER signature."], 201 | [[["b1dbc81696c8a9c0fccd0693ab66d7c368dbc38c0def4e800685560ddd1b2132", 0, "DUP HASH160 0x14 0x4b3bd7eba3bc0284fd3007be7f3be275e94f5826 EQUALVERIFY CHECKSIG"]], 202 | "010000000132211bdd0d568506804eef0d8cc3db68c3d766ab9306cdfcc0a9c89616c8dbb1000000006c493045022100c7bb0faea0522e74ff220c20c022d2cb6033f8d167fb89e75a50e237a35fd6d202203064713491b1f8ad5f79e623d0219ad32510bfaa1009ab30cbee77b59317d6e30001210237af13eb2d84e4545af287b919c2282019c9691cc509e78e196a9d8274ed1be0ffffffff0100000000000000001976a914f1b3ed2eda9a2ebe5a9374f692877cdf87c0f95b88ac00000000", "P2SH,DERSIG"], 203 | 204 | ["CHECKSEQUENCEVERIFY tests"], 205 | 206 | ["By-height locks, with argument just beyond txin.nSequence"], 207 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1 NOP3 1"]], 208 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 209 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], 210 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 211 | 212 | ["By-time locks, with argument just beyond txin.nSequence (but within numerical boundries)"], 213 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194305 NOP3 1"]], 214 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 215 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], 216 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000feff40000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 217 | 218 | ["Argument missing"], 219 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "NOP3 1"]], 220 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 221 | 222 | ["Argument negative with by-blockheight txin.nSequence=0"], 223 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP3 1"]], 224 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 225 | 226 | ["Argument negative with by-blocktime txin.nSequence=CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG"], 227 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 NOP3 1"]], 228 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 229 | 230 | ["Argument/tx height/time mismatch, both versions"], 231 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], 232 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 233 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "65535 NOP3 1"]], 234 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 235 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 NOP3 1"]], 236 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 237 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4259839 NOP3 1"]], 238 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 239 | 240 | ["6 byte non-minimally-encoded arguments are invalid even if their contents are valid"], 241 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x06 0x000000000000 NOP3 1"]], 242 | "020000000100010000000000000000000000000000000000000000000000000000000000000000000000ffff00000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 243 | 244 | ["Failure due to failing CHECKSEQUENCEVERIFY in scriptSig"], 245 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]], 246 | "02000000010001000000000000000000000000000000000000000000000000000000000000000000000251b2000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 247 | 248 | ["Failure due to failing CHECKSEQUENCEVERIFY in redeemScript"], 249 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7c17aff532f22beb54069942f9bf567a66133eaf EQUAL"]], 250 | "0200000001000100000000000000000000000000000000000000000000000000000000000000000000030251b2000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 251 | 252 | ["Failure due to insufficient tx.nVersion (<2)"], 253 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 NOP3 1"]], 254 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 255 | [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "4194304 NOP3 1"]], 256 | "010000000100010000000000000000000000000000000000000000000000000000000000000000000000000040000100000000000000000000000000", "P2SH,CHECKSEQUENCEVERIFY"], 257 | 258 | ["Make diffs cleaner by leaving a comment here without comma at the end"] 259 | ] 260 | -------------------------------------------------------------------------------- /tests/data/valid_wifs.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", 4 | "65a16059864a2fdbc7c99a4723a8395bc6f188eb", 5 | { 6 | "addrType": "pubkey", 7 | "isPrivkey": false, 8 | "isTestnet": false 9 | } 10 | ], 11 | [ 12 | "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", 13 | "74f209f6ea907e2ea48f74fae05782ae8a665257", 14 | { 15 | "addrType": "script", 16 | "isPrivkey": false, 17 | "isTestnet": false 18 | } 19 | ], 20 | [ 21 | "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", 22 | "53c0307d6851aa0ce7825ba883c6bd9ad242b486", 23 | { 24 | "addrType": "pubkey", 25 | "isPrivkey": false, 26 | "isTestnet": true 27 | } 28 | ], 29 | [ 30 | "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", 31 | "6349a418fc4578d10a372b54b45c280cc8c4382f", 32 | { 33 | "addrType": "script", 34 | "isPrivkey": false, 35 | "isTestnet": true 36 | } 37 | ], 38 | [ 39 | "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", 40 | "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", 41 | { 42 | "isCompressed": false, 43 | "isPrivkey": true, 44 | "isTestnet": false 45 | } 46 | ], 47 | [ 48 | "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", 49 | "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", 50 | { 51 | "isCompressed": true, 52 | "isPrivkey": true, 53 | "isTestnet": false 54 | } 55 | ], 56 | [ 57 | "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", 58 | "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", 59 | { 60 | "isCompressed": false, 61 | "isPrivkey": true, 62 | "isTestnet": true 63 | } 64 | ], 65 | [ 66 | "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", 67 | "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", 68 | { 69 | "isCompressed": true, 70 | "isPrivkey": true, 71 | "isTestnet": true 72 | } 73 | ], 74 | [ 75 | "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", 76 | "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", 77 | { 78 | "addrType": "pubkey", 79 | "isPrivkey": false, 80 | "isTestnet": false 81 | } 82 | ], 83 | [ 84 | "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", 85 | "fcc5460dd6e2487c7d75b1963625da0e8f4c5975", 86 | { 87 | "addrType": "script", 88 | "isPrivkey": false, 89 | "isTestnet": false 90 | } 91 | ], 92 | [ 93 | "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", 94 | "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", 95 | { 96 | "addrType": "pubkey", 97 | "isPrivkey": false, 98 | "isTestnet": true 99 | } 100 | ], 101 | [ 102 | "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", 103 | "c579342c2c4c9220205e2cdc285617040c924a0a", 104 | { 105 | "addrType": "script", 106 | "isPrivkey": false, 107 | "isTestnet": true 108 | } 109 | ], 110 | [ 111 | "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", 112 | "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", 113 | { 114 | "isCompressed": false, 115 | "isPrivkey": true, 116 | "isTestnet": false 117 | } 118 | ], 119 | [ 120 | "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", 121 | "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", 122 | { 123 | "isCompressed": true, 124 | "isPrivkey": true, 125 | "isTestnet": false 126 | } 127 | ], 128 | [ 129 | "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", 130 | "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", 131 | { 132 | "isCompressed": false, 133 | "isPrivkey": true, 134 | "isTestnet": true 135 | } 136 | ], 137 | [ 138 | "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", 139 | "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", 140 | { 141 | "isCompressed": true, 142 | "isPrivkey": true, 143 | "isTestnet": true 144 | } 145 | ], 146 | [ 147 | "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", 148 | "7987ccaa53d02c8873487ef919677cd3db7a6912", 149 | { 150 | "addrType": "pubkey", 151 | "isPrivkey": false, 152 | "isTestnet": false 153 | } 154 | ], 155 | [ 156 | "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", 157 | "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", 158 | { 159 | "addrType": "script", 160 | "isPrivkey": false, 161 | "isTestnet": false 162 | } 163 | ], 164 | [ 165 | "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", 166 | "ef66444b5b17f14e8fae6e7e19b045a78c54fd79", 167 | { 168 | "addrType": "pubkey", 169 | "isPrivkey": false, 170 | "isTestnet": true 171 | } 172 | ], 173 | [ 174 | "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", 175 | "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", 176 | { 177 | "addrType": "script", 178 | "isPrivkey": false, 179 | "isTestnet": true 180 | } 181 | ], 182 | [ 183 | "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", 184 | "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", 185 | { 186 | "isCompressed": false, 187 | "isPrivkey": true, 188 | "isTestnet": false 189 | } 190 | ], 191 | [ 192 | "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", 193 | "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", 194 | { 195 | "isCompressed": true, 196 | "isPrivkey": true, 197 | "isTestnet": false 198 | } 199 | ], 200 | [ 201 | "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", 202 | "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", 203 | { 204 | "isCompressed": false, 205 | "isPrivkey": true, 206 | "isTestnet": true 207 | } 208 | ], 209 | [ 210 | "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", 211 | "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", 212 | { 213 | "isCompressed": true, 214 | "isPrivkey": true, 215 | "isTestnet": true 216 | } 217 | ], 218 | [ 219 | "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", 220 | "adc1cc2081a27206fae25792f28bbc55b831549d", 221 | { 222 | "addrType": "pubkey", 223 | "isPrivkey": false, 224 | "isTestnet": false 225 | } 226 | ], 227 | [ 228 | "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", 229 | "188f91a931947eddd7432d6e614387e32b244709", 230 | { 231 | "addrType": "script", 232 | "isPrivkey": false, 233 | "isTestnet": false 234 | } 235 | ], 236 | [ 237 | "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", 238 | "1694f5bc1a7295b600f40018a618a6ea48eeb498", 239 | { 240 | "addrType": "pubkey", 241 | "isPrivkey": false, 242 | "isTestnet": true 243 | } 244 | ], 245 | [ 246 | "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", 247 | "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", 248 | { 249 | "addrType": "script", 250 | "isPrivkey": false, 251 | "isTestnet": true 252 | } 253 | ], 254 | [ 255 | "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", 256 | "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", 257 | { 258 | "isCompressed": false, 259 | "isPrivkey": true, 260 | "isTestnet": false 261 | } 262 | ], 263 | [ 264 | "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", 265 | "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", 266 | { 267 | "isCompressed": true, 268 | "isPrivkey": true, 269 | "isTestnet": false 270 | } 271 | ], 272 | [ 273 | "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", 274 | "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", 275 | { 276 | "isCompressed": false, 277 | "isPrivkey": true, 278 | "isTestnet": true 279 | } 280 | ], 281 | [ 282 | "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", 283 | "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", 284 | { 285 | "isCompressed": true, 286 | "isPrivkey": true, 287 | "isTestnet": true 288 | } 289 | ], 290 | [ 291 | "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", 292 | "c4c1b72491ede1eedaca00618407ee0b772cad0d", 293 | { 294 | "addrType": "pubkey", 295 | "isPrivkey": false, 296 | "isTestnet": false 297 | } 298 | ], 299 | [ 300 | "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", 301 | "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", 302 | { 303 | "addrType": "script", 304 | "isPrivkey": false, 305 | "isTestnet": false 306 | } 307 | ], 308 | [ 309 | "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", 310 | "261f83568a098a8638844bd7aeca039d5f2352c0", 311 | { 312 | "addrType": "pubkey", 313 | "isPrivkey": false, 314 | "isTestnet": true 315 | } 316 | ], 317 | [ 318 | "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", 319 | "e930e1834a4d234702773951d627cce82fbb5d2e", 320 | { 321 | "addrType": "script", 322 | "isPrivkey": false, 323 | "isTestnet": true 324 | } 325 | ], 326 | [ 327 | "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", 328 | "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", 329 | { 330 | "isCompressed": false, 331 | "isPrivkey": true, 332 | "isTestnet": false 333 | } 334 | ], 335 | [ 336 | "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", 337 | "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", 338 | { 339 | "isCompressed": true, 340 | "isPrivkey": true, 341 | "isTestnet": false 342 | } 343 | ], 344 | [ 345 | "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", 346 | "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", 347 | { 348 | "isCompressed": false, 349 | "isPrivkey": true, 350 | "isTestnet": true 351 | } 352 | ], 353 | [ 354 | "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", 355 | "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", 356 | { 357 | "isCompressed": true, 358 | "isPrivkey": true, 359 | "isTestnet": true 360 | } 361 | ], 362 | [ 363 | "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", 364 | "5eadaf9bb7121f0f192561a5a62f5e5f54210292", 365 | { 366 | "addrType": "pubkey", 367 | "isPrivkey": false, 368 | "isTestnet": false 369 | } 370 | ], 371 | [ 372 | "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", 373 | "3f210e7277c899c3a155cc1c90f4106cbddeec6e", 374 | { 375 | "addrType": "script", 376 | "isPrivkey": false, 377 | "isTestnet": false 378 | } 379 | ], 380 | [ 381 | "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", 382 | "c8a3c2a09a298592c3e180f02487cd91ba3400b5", 383 | { 384 | "addrType": "pubkey", 385 | "isPrivkey": false, 386 | "isTestnet": true 387 | } 388 | ], 389 | [ 390 | "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", 391 | "99b31df7c9068d1481b596578ddbb4d3bd90baeb", 392 | { 393 | "addrType": "script", 394 | "isPrivkey": false, 395 | "isTestnet": true 396 | } 397 | ], 398 | [ 399 | "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", 400 | "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", 401 | { 402 | "isCompressed": false, 403 | "isPrivkey": true, 404 | "isTestnet": false 405 | } 406 | ], 407 | [ 408 | "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", 409 | "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", 410 | { 411 | "isCompressed": true, 412 | "isPrivkey": true, 413 | "isTestnet": false 414 | } 415 | ], 416 | [ 417 | "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", 418 | "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", 419 | { 420 | "isCompressed": false, 421 | "isPrivkey": true, 422 | "isTestnet": true 423 | } 424 | ], 425 | [ 426 | "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", 427 | "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", 428 | { 429 | "isCompressed": true, 430 | "isPrivkey": true, 431 | "isTestnet": true 432 | } 433 | ], 434 | [ 435 | "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", 436 | "1ed467017f043e91ed4c44b4e8dd674db211c4e6", 437 | { 438 | "addrType": "pubkey", 439 | "isPrivkey": false, 440 | "isTestnet": false 441 | } 442 | ], 443 | [ 444 | "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", 445 | "5ece0cadddc415b1980f001785947120acdb36fc", 446 | { 447 | "addrType": "script", 448 | "isPrivkey": false, 449 | "isTestnet": false 450 | } 451 | ] 452 | ] -------------------------------------------------------------------------------- /tests/script/test_interpreter.py: -------------------------------------------------------------------------------- 1 | from pytest import raises, fixture, fail 2 | import bitforge.networks 3 | from bitforge.privkey import PrivateKey 4 | from bitforge.pubkey import PublicKey 5 | from bitforge.script import Interpreter, Script 6 | from bitforge.encoding import encode_int, decode_hex, encode_script_number 7 | from bitforge.script.opcode import * 8 | 9 | 10 | class TestInterpreter: 11 | 12 | def test_init_interpreter(self): 13 | interpreter = Interpreter() 14 | 15 | assert len(interpreter.stack) == 0 16 | assert len(interpreter.altstack) == 0 17 | assert len(interpreter.vf_exec) == 0 18 | assert interpreter.pc == 0 19 | assert interpreter.pbegincodehash == 0 20 | assert interpreter.nop_count == 0 21 | assert interpreter.errstr == '' 22 | assert interpreter.flags == 0 23 | 24 | def test_cast_to_bool(self): 25 | assert Interpreter.cast_to_bool(encode_script_number(0)) is False 26 | assert Interpreter.cast_to_bool(decode_hex('008A')) is False # Negative 0 27 | assert Interpreter.cast_to_bool(encode_script_number(1)) is True 28 | assert Interpreter.cast_to_bool(encode_script_number(-1)) is True 29 | 30 | def test_verify_trivial_scripts(self): 31 | interpreter = Interpreter() 32 | 33 | verified = interpreter.verify(Script.compile([OP_1]), Script.compile([OP_1])) 34 | assert verified is True 35 | 36 | verified = interpreter.verify(Script.compile([OP_1]), Script.compile([OP_0])) 37 | assert verified is False 38 | 39 | verified = interpreter.verify(Script.compile([OP_0]), Script.compile([OP_1])) 40 | assert verified is True 41 | 42 | verified = interpreter.verify(Script.compile([OP_CODESEPARATOR]), Script.compile([OP_1])) 43 | assert verified is True 44 | 45 | verified = interpreter.verify(Script(), Script.compile([OP_DEPTH, OP_0, OP_EQUAL])) 46 | assert verified is True 47 | 48 | # verified = interpreter.verify(Script.from_string('9 0x000000000000000010'), Script()) 49 | # assert verified is True 50 | 51 | verified = interpreter.verify(Script.compile([OP_1]), Script.compile([OP_15, OP_ADD, OP_16, OP_EQUAL])) 52 | assert verified is True 53 | 54 | verified = interpreter.verify(Script.compile([OP_0]), Script.compile([OP_IF, OP_VERIFY, OP_ELSE, OP_1, OP_ENDIF])) 55 | assert verified is True 56 | 57 | 58 | 59 | # def test_from_hex_errors(self): 60 | # with raises(PublicKey.InvalidHex): PublicKey.from_hex('a') 61 | # with raises(PublicKey.InvalidHex): PublicKey.from_hex('a@') 62 | 63 | # def test_to_hex_uncompressed(self): 64 | # string = '04b805174bd496b275e711d5a9f1bcbaa4bba1a77176dbdb5fdd8b769da62a36a9c3dfa7c8ccb509f9a66efd6d8d1db6b25aa7c100476154b6303d76c28eda099b' 65 | # assert PublicKey.from_hex(string).to_hex() == string 66 | -------------------------------------------------------------------------------- /tests/script/test_opcode.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from pytest import raises 4 | 5 | from bitforge.script.opcode import * 6 | 7 | 8 | class TestOpcode: 9 | 10 | def test_create(self): 11 | assert Opcode(80) == OP_RESERVED 12 | 13 | def test_create_invalid(self): 14 | with raises(Opcode.UnknownOpcodeNumber): 15 | Opcode(256) 16 | 17 | def test_name(self): 18 | assert Opcode.const_push_for(3).name == '_PUSH_3_BYTES' 19 | assert OP_0.name == 'OP_0' 20 | assert OP_RESERVED.name == 'OP_RESERVED' 21 | 22 | def test_is_number(self): 23 | assert OP_0.is_number() is True 24 | assert OP_16.is_number() is True 25 | assert OP_RESERVED.is_number() is False 26 | 27 | def test_number_value(self): 28 | with raises(Opcode.WrongOpcodeType): 29 | OP_RESERVED.number_value() 30 | 31 | assert OP_0.number_value() == 0 32 | assert OP_16.number_value() == 16 33 | 34 | def test_is_push(self): 35 | assert Opcode.const_push_for(10).is_push() is True 36 | assert Opcode.var_push_for(36).is_push() is True 37 | assert OP_RESERVED.is_push() is False 38 | 39 | def test_is_const_push(self): 40 | assert Opcode(75).is_const_push() is True 41 | assert OP_PUSHDATA1.is_const_push() is False 42 | 43 | def test_is_var_push(self): 44 | for opcode in (OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4): 45 | assert opcode.is_var_push() is True 46 | 47 | assert OP_RESERVED.is_var_push() is False 48 | 49 | def test_ordering(self): 50 | assert OP_0 < OP_1 51 | assert OP_1 > OP_0 52 | 53 | def test_bytes(self): 54 | assert OP_1.bytes == b'\x51' 55 | 56 | def test_for_number(self): 57 | with raises(ValueError): 58 | Opcode.for_number(17) 59 | assert Opcode.for_number(10) == Opcode(90) 60 | 61 | def test_from_name(self): 62 | with raises(Opcode.UnknownOpcodeName): 63 | Opcode.from_name('OP_FOO') 64 | 65 | assert Opcode.from_name('OP_1') == OP_1 66 | 67 | def test_const_push_for(self): 68 | with raises(Opcode.InvalidConstPushLength): 69 | Opcode.const_push_for(0) 70 | 71 | with raises(Opcode.InvalidConstPushLength): 72 | Opcode.const_push_for(76) 73 | 74 | assert Opcode.const_push_for(10) == Opcode(10) 75 | 76 | def test_var_push_for(self): 77 | with raises(Opcode.InvalidPushLength): 78 | Opcode.var_push_for(0) 79 | 80 | assert Opcode.var_push_for(1) == OP_PUSHDATA1 81 | assert Opcode.var_push_for(1 << 15) == OP_PUSHDATA2 82 | assert Opcode.var_push_for(1 << 31) == OP_PUSHDATA4 83 | 84 | with raises(Opcode.InvalidPushLength): 85 | Opcode.var_push_for(1 << 64) 86 | 87 | def test_data_length_max(self): 88 | assert Opcode.data_length_max(Opcode.const_push_for(5)) == 5 89 | assert Opcode.data_length_max(OP_RESERVED) == 0 90 | assert Opcode.data_length_max(OP_PUSHDATA1) == 255 91 | 92 | def test_data_length_nbytes(opcode): 93 | assert Opcode.data_length_nbytes(OP_PUSHDATA1) == 1 94 | assert Opcode.data_length_nbytes(OP_PUSHDATA2) == 2 95 | assert Opcode.data_length_nbytes(OP_PUSHDATA4) == 4 96 | 97 | with raises(Opcode.WrongOpcodeType): 98 | Opcode.data_length_nbytes(OP_0) 99 | -------------------------------------------------------------------------------- /tests/script/test_script.py: -------------------------------------------------------------------------------- 1 | import pytest, inspect 2 | from pytest import raises, fixture, fail 3 | 4 | from bitforge.script import * 5 | from bitforge.script.script import SCRIPT_SUBCLASSES 6 | from bitforge.script.opcode import * 7 | import bitforge.script.opcode as opcode_module 8 | from bitforge.encoding import * 9 | from bitforge.tools import Buffer 10 | from bitforge import Address, PrivateKey 11 | 12 | 13 | class TestScript: 14 | def test_create_emtpy(self): 15 | s = Script() 16 | assert s.instructions == tuple() 17 | 18 | def test_binary_single(self): 19 | s = Script.from_bytes(b'\0') 20 | assert s.instructions == (Instruction(OP_0),) 21 | 22 | def test_binary_const_pushes(self): 23 | for length in range(1, 76): 24 | opcode = Opcode(length) 25 | string = b'a' * length 26 | 27 | op_bytes = bytes(bytearray([length])) 28 | s = Script.from_bytes(op_bytes + string) 29 | 30 | assert s.instructions == (Instruction(opcode, string),) 31 | 32 | def test_binary_var_pushes(self): 33 | s1 = Script.from_bytes(OP_PUSHDATA1.bytes + b'\3' + b'abc') 34 | assert s1.instructions == (Instruction(OP_PUSHDATA1, b'abc'),) 35 | 36 | s2 = Script.from_bytes(OP_PUSHDATA2.bytes + b'\3\0' + b'abc') 37 | assert s2.instructions == (Instruction(OP_PUSHDATA2, b'abc'),) 38 | 39 | s2 = Script.from_bytes(OP_PUSHDATA4.bytes + b'\3\0\0\0' + b'abc') 40 | assert s2.instructions == (Instruction(OP_PUSHDATA4, b'abc'),) 41 | 42 | with raises(Buffer.InsufficientData): 43 | Script.from_bytes(OP_PUSHDATA1.bytes + b'\3' + b'a') 44 | 45 | with raises(Buffer.InsufficientData): 46 | Script.from_bytes(OP_PUSHDATA2.bytes + b'\3\0' + b'a') 47 | 48 | with raises(Buffer.InsufficientData): 49 | Script.from_bytes(OP_PUSHDATA4.bytes + b'\3\0\0\0' + b'a') 50 | 51 | def test_binary_all_nonpush_opcodes(self): 52 | opcodes = [] 53 | for name, value in inspect.getmembers(opcode_module): 54 | if isinstance(value, Opcode) and not value.is_push(): 55 | opcodes.append(value) 56 | 57 | bytes = b''.join(opcode.bytes for opcode in opcodes) 58 | 59 | s = Script.from_bytes(bytes) 60 | assert s.instructions == tuple(map(Instruction, opcodes)) 61 | 62 | def test_from_string(self): 63 | 64 | def test_script_string(string, instructions): 65 | script = Script.from_string(string) 66 | assert script.to_string() == string 67 | assert len(script.instructions) == instructions 68 | 69 | test_script_string('OP_0 OP_PUSHDATA4 3 0x010203 OP_0', 3) 70 | test_script_string('OP_0 OP_PUSHDATA2 3 0x010203 OP_0', 3) 71 | test_script_string('OP_0 OP_PUSHDATA1 3 0x010203 OP_0', 3) 72 | test_script_string('OP_0 3 0x010203 OP_0', 3) 73 | 74 | with raises(Script.UnknownOpcodeName): 75 | Script.from_string('OP_99') 76 | 77 | with raises(Script.MissingPushArguments): 78 | Script.from_string('OP_PUSHDATA1') 79 | 80 | with raises(Script.MissingPushArguments): 81 | Script.from_string('OP_PUSHDATA1 3') 82 | 83 | with raises(Script.InvalidPushDataLength): 84 | Script.from_string('OP_PUSHDATA1 3 0x01020302') 85 | 86 | with raises(Script.InvalidPushData): 87 | Script.from_string('OP_PUSHDATA1 3 010203') 88 | 89 | with raises(Script.InvalidPushData): 90 | Script.from_string('3 010203') 91 | 92 | def test_is_push_only(self): 93 | script = Script.from_string('OP_1 OP_16') 94 | assert script.is_push_only() is True 95 | 96 | script = Script.from_string('OP_PUSHDATA1 1 0x01') 97 | assert script.is_push_only() is True 98 | 99 | script = Script.from_string('OP_1 OP_RETURN') 100 | assert script.is_push_only() is False 101 | 102 | def test_is_pay_to_pubkey_in(self): 103 | address = PrivateKey().to_address() 104 | yes = [ 105 | PayToPubkeyIn.create(address, b'foo'), 106 | PayToPubkeyIn.create(address, b'bar') 107 | ] 108 | 109 | no = [ 110 | Script(), 111 | PayToPubkeyOut.create(address) 112 | ] 113 | 114 | assert all(map(PayToPubkeyIn.is_valid, yes)) 115 | assert not any(map(PayToPubkeyIn.is_valid, no)) 116 | 117 | def test_is_pay_to_pubkey_out(self): 118 | address = PrivateKey().to_address() 119 | yes = [ 120 | PayToPubkeyOut.create(address), 121 | PayToPubkeyOut.create(address) 122 | ] 123 | 124 | no = [ 125 | Script(), 126 | PayToPubkeyIn.create(address, b'foo') 127 | ] 128 | 129 | assert all(map(PayToPubkeyOut.is_valid, yes)) 130 | assert not any(map(PayToPubkeyOut.is_valid, no)) 131 | 132 | def test_is_pay_to_script_out(self): 133 | address = PrivateKey().to_address() 134 | embedded = PayToPubkeyOut.create(address) 135 | 136 | yes = [ PayToScriptOut.create(embedded) ] 137 | 138 | no = [ 139 | Script(), 140 | PayToPubkeyOut.create(address), 141 | PayToScriptIn.create(embedded, [ b'foo' ]) 142 | ] 143 | 144 | assert all(map(PayToScriptOut.is_valid, yes)) 145 | assert not any(map(PayToScriptOut.is_valid, no)) 146 | 147 | def test_is_pay_to_script_in(self): 148 | address = PrivateKey().to_address() 149 | embedded = PayToPubkeyOut.create(address) 150 | 151 | yes = [ 152 | PayToScriptIn.create(embedded, [ b'foo' ]), 153 | PayToScriptIn.create(Script.compile([ b'bar' ]), [ b'foo' ]), 154 | PayToScriptIn.create(Script.compile([ b'baz' ]), [ b'one', b'two' ]), 155 | ] 156 | 157 | no = [ 158 | Script(), 159 | PayToPubkeyIn.create(address, b'foo'), 160 | PayToScriptOut.create(Script()) 161 | ] 162 | 163 | assert all(map(PayToScriptIn.is_valid, yes)) 164 | assert not any(map(PayToScriptIn.is_valid, no)) 165 | 166 | def test_p2pkh_getters(self): 167 | privkey = PrivateKey() 168 | pubkey = privkey.to_public_key() 169 | address = pubkey.to_address() 170 | signature = b'foo' 171 | 172 | i_script = PayToPubkeyIn.create(pubkey, signature) 173 | o_script = PayToPubkeyOut.create(address) 174 | 175 | assert i_script.get_public_key() == pubkey 176 | assert i_script.get_signature() == signature 177 | assert o_script.get_address_hash() == address.phash 178 | 179 | def test_p2sh_getters(self): 180 | privkey = PrivateKey() 181 | pubkey = privkey.to_public_key() 182 | address = pubkey.to_address() 183 | embedded = Script.compile([ OP_0, b'bar', OP_1 ]) 184 | signatures = [ b'foo', b'bar' ] 185 | 186 | i_script = PayToScriptIn.create(embedded, signatures) 187 | o_script = PayToScriptOut.create(embedded) 188 | 189 | assert i_script.get_script() == embedded 190 | assert i_script.get_signatures() == signatures 191 | assert o_script.get_script_hash() == embedded.to_hash() 192 | 193 | def test_op_return_getters(self): 194 | data = b'foo bar baz' 195 | script = OpReturnOut.create(data) 196 | assert script.get_data() == data 197 | 198 | def test_redeem_multisig_getters(self): 199 | privkeys = [ PrivateKey(), PrivateKey() ] 200 | pubkeys = [ privkey.to_public_key() for privkey in privkeys ] 201 | signatures = [ b'foo', b'bar' ] 202 | 203 | script = RedeemMultisig.create(pubkeys, 2) 204 | 205 | assert script.get_min_signatures() == 2 206 | assert script.get_public_keys() == pubkeys 207 | 208 | def test_classify(self): 209 | privkey = PrivateKey() 210 | pubkey = privkey.to_public_key() 211 | address = pubkey.to_address() 212 | signature = b'foo' 213 | script = RedeemMultisig.create([ pubkey ], 1) 214 | 215 | class_to_arguments = { 216 | PayToPubkeyIn : [ pubkey, signature ], 217 | PayToPubkeyOut: [ address ], 218 | PayToScriptIn : [ script, [signature] ], 219 | PayToScriptOut: [ script ], 220 | RedeemMultisig: [ [pubkey], 1 ], 221 | OpReturnOut : [ b'data' ] 222 | } 223 | 224 | for cls, args in class_to_arguments.items(): 225 | special = cls.create(*args) 226 | generic = Script(special.instructions) 227 | 228 | assert Script.classify(special) == Script.classify(generic) == cls 229 | assert isinstance(Script.create(special.instructions), cls) 230 | -------------------------------------------------------------------------------- /tests/test_address.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import json 3 | 4 | from pytest import raises, fixture, fail 5 | 6 | from bitforge import networks, Address, PublicKey 7 | from bitforge.encoding import * 8 | 9 | 10 | data = { 11 | 'base58h': 'mz7Rb837TrRMBxSNV8ZqRysS1JCDPWFLCc', 12 | 'hex' : '6fcbf730e06e5f8e4fc44f071d436a4660ddde3e47', 13 | 'phash' : 'cbf730e06e5f8e4fc44f071d436a4660ddde3e47', 14 | 'network': networks.testnet, 15 | 'type' : Address.Type.PublicKey, 16 | 'pubkey' : [ 17 | 2505267213527803793801554682227237457256110293342017361806815033635284562140, 18 | 62983861414933912325775225572939498310844638563302563929329670771752596415141 19 | ] 20 | } 21 | 22 | 23 | def read_addr_fixture_info(info): 24 | string_b58h, string_hex, meta = info 25 | 26 | network = networks.testnet if meta['isTestnet'] else networks.livenet 27 | type = Address.Type.PublicKey if meta['addrType'] == 'pubkey' else Address.Type.Script 28 | 29 | return (string_b58h, string_hex, network, type) 30 | 31 | 32 | @fixture 33 | def valid_addresses(): 34 | with open('tests/data/valid_wifs.json') as f: 35 | return [read_addr_fixture_info(e) for e in json.load(f) if not e[2]['isPrivkey']] 36 | 37 | 38 | class TestAddress: 39 | def test_create(self): 40 | Address('a' * 20, networks.livenet, Address.Type.PublicKey) 41 | Address('a' * 20, networks.livenet, Address.Type.Script) 42 | Address('a' * 20, networks.testnet, Address.Type.PublicKey) 43 | Address('a' * 20, networks.testnet, Address.Type.Script) 44 | 45 | def test_invalid_phash(self): 46 | with raises(Address.InvalidHashLength): 47 | Address('a') 48 | 49 | def test_invalid_network(self): 50 | with raises(Address.UnknownNetwork): 51 | Address('a' * 20, -1) 52 | 53 | def test_invalid_version(self): 54 | with raises(Address.InvalidVersion): 55 | Address.from_bytes(b'\x0f' + b'a' * 20) 56 | 57 | def test_invalid_type(self): 58 | with raises(Address.InvalidType): 59 | Address('a' * 20, networks.livenet, None) 60 | 61 | def test_from_hex(self): 62 | address = Address.from_hex(data['hex']) 63 | 64 | assert address.network is data['network'] 65 | assert address.phash == decode_hex(data['phash']) 66 | assert address.type == data['type'] 67 | assert address.to_string() == data['base58h'] 68 | 69 | def test_from_invalid_hex(self): 70 | with raises(Address.InvalidHex): Address.from_hex('a') 71 | with raises(Address.InvalidHex): Address.from_hex('a@') 72 | 73 | def test_from_string(self): 74 | address = Address.from_string(data['base58h']) 75 | 76 | assert address.network is data['network'] 77 | assert address.phash == decode_hex(data['phash']) 78 | assert address.type == data['type'] 79 | assert address.to_hex() == data['hex'] 80 | 81 | def test_from_invalid_string(self): 82 | with raises(Address.InvalidBase58h): Address.from_string('a') 83 | with raises(Address.InvalidBase58h): Address.from_string('a@') 84 | 85 | def test_from_invalid_bytes(self): 86 | with raises(Address.InvalidBinaryLength): 87 | Address.from_bytes('a') 88 | 89 | 90 | def test_from_pubkey(self): 91 | pubkey = PublicKey(data['pubkey'], data['network']) 92 | address = Address.from_public_key(pubkey) 93 | 94 | assert address == Address.from_string(data['base58h']) 95 | 96 | 97 | def test_bitcoind_addresses(self, valid_addresses): 98 | for string_b58h, string_hex, network, type in valid_addresses: 99 | address = Address.from_string(string_b58h) 100 | 101 | assert address.network is network 102 | assert address.type == type 103 | assert address.to_string() == string_b58h 104 | assert encode_hex(address.phash) == string_hex.encode('utf-8') 105 | -------------------------------------------------------------------------------- /tests/test_privkey.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pytest import raises, fixture, fail 3 | 4 | from bitforge import networks 5 | from bitforge.encoding import * 6 | from bitforge.privkey import PrivateKey 7 | 8 | data = { 9 | 'privkey_hex' : 'f04da984a7d553a0ac51b50bf92d2257d46f65286f2d5da5b83f8ccc114393a7', 10 | 'privkey_bin' : b'\xf0M\xa9\x84\xa7\xd5S\xa0\xacQ\xb5\x0b\xf9-"W\xd4oe(o-]\xa5\xb8?\x8c\xcc\x11C\x93\xa7', 11 | 'pubkey': { 12 | 'compress_hex' : '02e9af68f090bdb18997b676103794e7ed43f9148e882f300d9173c7aac5d497d2', 13 | 'uncompress_hex': '04e9af68f090bdb18997b676103794e7ed43f9148e882f300d9173c7aac5d497d26e4b866169626d83f6230cdc90e0c62a0ae7017579368cb870eb83dcaa1fec3a', 14 | }, 15 | 'wif' : { 16 | 'live_compress' : 'L5Gq3mntBKNR9inFjbzesJt2ziboDqjc2iK7Aj2qiy85goAXcjPV', 17 | 'live_uncompress': '5Ke7or7mg3MFzFuPpiTf2tBCnFQk6dR9qsbTmoE74AYWcQ8FmJv', 18 | 'test_compress' : 'cVdpWgnjcP4gKAFX81onEdP6cwuCtHqJ6kTaH9VME5n5wYCzH5xU', 19 | 'test_uncompress': '93QkPawKGGRPxKQgT4MZuUjARumTFnxMBpTQrRacPuHZPQdgS1D', 20 | }, 21 | 'address': { 22 | 'live_compress' : '1LjsiHFYCbXjCw4NCTR6AxakMqM3sUZqEf', 23 | 'live_uncompress': '18zBfQUkeg9VZFWf53ApjtNgnJDhzdpMfR', 24 | 'test_compress' : 'n1Fq1LLX1cxyz3Xyv2PTzso5Dpwkk1s86J', 25 | 'test_uncompress': 'moW8xTZjThakLMzGnc9CZob1eHpQsTNQN6', 26 | } 27 | } 28 | 29 | 30 | @fixture 31 | def valid_wifs(): 32 | with open('tests/data/valid_wifs.json') as f: 33 | return [ item for item in json.load(f) if item[2]['isPrivkey'] ] 34 | 35 | @fixture 36 | def invalid_wifs(): 37 | with open('tests/data/invalid_wifs.json') as f: 38 | return [ item[0] for item in json.load(f) ] 39 | 40 | 41 | class TestPrivateKey: 42 | 43 | def test_from_random(self): 44 | k1, k2 = PrivateKey(), PrivateKey() 45 | assert k1.secret != k2.secret 46 | 47 | 48 | def test_invalid_secret(self): 49 | with raises(PrivateKey.InvalidSecret): PrivateKey(-1) 50 | with raises(PrivateKey.InvalidSecret): PrivateKey(10 ** 100) 51 | 52 | 53 | def test_invalid_network(self): 54 | with raises(PrivateKey.UnknownNetwork): 55 | PrivateKey(network = -1) 56 | 57 | 58 | def test_from_hex(self): 59 | k = PrivateKey.from_hex(data['privkey_hex']) 60 | 61 | assert k.to_hex() == data['privkey_hex'] 62 | assert k.to_bytes() == data['privkey_bin'] 63 | 64 | assert k.compressed is True 65 | assert k.network is networks.default 66 | 67 | 68 | def test_from_invalid_hex(self): 69 | with raises(PrivateKey.InvalidHex): PrivateKey.from_hex('a') 70 | with raises(PrivateKey.InvalidHex): PrivateKey.from_hex('a@') 71 | 72 | 73 | def test_from_bytes(self): 74 | k = PrivateKey.from_bytes(data['privkey_bin']) 75 | 76 | assert k.to_hex() == data['privkey_hex'] 77 | assert k.to_bytes() == data['privkey_bin'] 78 | 79 | assert k.compressed is True 80 | assert k.network is networks.default 81 | 82 | 83 | def test_from_invalid_bytes(self): 84 | with raises(PrivateKey.InvalidBinaryLength): 85 | PrivateKey.from_bytes('a') 86 | 87 | with raises(PrivateKey.InvalidBinaryLength): 88 | PrivateKey.from_bytes('a' * 33) 89 | 90 | 91 | def test_from_wif_live_compress(self): 92 | k = PrivateKey.from_wif(data['wif']['live_compress']) 93 | 94 | assert k.compressed is True 95 | assert k.network is networks.livenet 96 | assert k.to_wif() == data['wif']['live_compress'] 97 | 98 | 99 | def test_from_wif_test_compress(self): 100 | k = PrivateKey.from_wif(data['wif']['test_compress']) 101 | 102 | assert k.compressed is True 103 | assert k.network is networks.testnet 104 | assert k.to_wif() == data['wif']['test_compress'] 105 | 106 | 107 | def test_from_wif_live_uncompress(self): 108 | k = PrivateKey.from_wif(data['wif']['live_uncompress']) 109 | 110 | assert k.compressed is False 111 | assert k.network is networks.livenet 112 | assert k.to_wif() == data['wif']['live_uncompress'] 113 | 114 | 115 | def test_from_wif_test_uncompress(self): 116 | k = PrivateKey.from_wif(data['wif']['test_uncompress']) 117 | 118 | assert k.compressed is False 119 | assert k.network is networks.testnet 120 | assert k.to_wif() == data['wif']['test_uncompress'] 121 | 122 | 123 | def test_from_invalid_wif(self): 124 | too_short = encode_base58h(b'a') 125 | too_long = encode_base58h(b'a' * 30) 126 | 127 | with raises(PrivateKey.InvalidWifLength): PrivateKey.from_wif(too_short) 128 | with raises(PrivateKey.InvalidWifLength): PrivateKey.from_wif(too_long) 129 | 130 | valid = decode_base58h(PrivateKey().to_wif()) 131 | 132 | with raises(PrivateKey.InvalidCompressionByte): 133 | PrivateKey.from_wif(encode_base58h(valid[:-1] + b'a')) 134 | 135 | with raises(PrivateKey.UnknownNetwork): 136 | PrivateKey.from_wif(encode_base58h(b'a' + valid[1:])) 137 | 138 | 139 | def test_bitcoind_valid_wifs(self, valid_wifs): 140 | for wif, secret_hex, attrs in valid_wifs: 141 | secret = decode_int(decode_hex(secret_hex)) 142 | network = networks.testnet if attrs['isTestnet'] else networks.livenet 143 | compressed = attrs['isCompressed'] 144 | 145 | k = PrivateKey.from_wif(wif) 146 | 147 | assert k.secret == secret 148 | assert k.network is network 149 | assert k.compressed == compressed 150 | 151 | 152 | def test_bitcoind_invalid_wifs(self, invalid_wifs): 153 | for invalid_wif in invalid_wifs: 154 | with raises(PrivateKey.Error): 155 | PrivateKey.from_wif(invalid_wif) 156 | 157 | 158 | def test_to_pubkey_compressed(self): 159 | k = PrivateKey.from_wif(data['wif']['live_compress']) 160 | assert k.to_public_key().to_hex() == data['pubkey']['compress_hex'] 161 | 162 | 163 | def test_to_pubkey_compressed(self): 164 | k = PrivateKey.from_wif(data['wif']['live_uncompress']) 165 | assert k.to_public_key().to_hex() == data['pubkey']['uncompress_hex'] 166 | 167 | 168 | def test_to_address_live_compressed(self): 169 | k = PrivateKey.from_wif(data['wif']['live_compress']) 170 | assert k.to_address().to_string() == data['address']['live_compress'] 171 | 172 | 173 | def test_to_address_live_uncompressed(self): 174 | k = PrivateKey.from_wif(data['wif']['live_uncompress']) 175 | assert k.to_address().to_string() == data['address']['live_uncompress'] 176 | 177 | 178 | def test_to_address_test_compressed(self): 179 | k = PrivateKey.from_wif(data['wif']['test_compress']) 180 | assert k.to_address().to_string() == data['address']['test_compress'] 181 | 182 | 183 | def test_to_address_test_uncompressed(self): 184 | k = PrivateKey.from_wif(data['wif']['test_uncompress']) 185 | assert k.to_address().to_string() == data['address']['test_uncompress'] 186 | 187 | 188 | def test_roundtrip_wif(self): 189 | k1 = PrivateKey() 190 | k2 = PrivateKey.from_wif(k1.to_wif()) 191 | 192 | assert k1.secret == k2.secret 193 | assert k1.network is k2.network 194 | assert k1.compressed == k2.compressed 195 | -------------------------------------------------------------------------------- /tests/test_pubkey.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from pytest import raises, fixture, fail 4 | 5 | from bitforge.privkey import PrivateKey 6 | from bitforge.pubkey import PublicKey 7 | import bitforge.networks 8 | 9 | 10 | data = { 11 | 'privkey_hex': 'd862dc70f3a40b52e9ed3567b073e32dc543f3b51c9eae8f3ac3e95a05af6b65', 12 | 'pubkey_pair': ( 13 | 83234559159195082631296919245646869202106616233090297833190812666019583768233, 14 | 88596170374369955503701867101393570128177433697625675006047409032754219321755 15 | ), 16 | 'pubkey_bin' : b'\x03\xb8\x05\x17K\xd4\x96\xb2u\xe7\x11\xd5\xa9\xf1\xbc\xba\xa4\xbb\xa1\xa7qv\xdb\xdb_\xdd\x8bv\x9d\xa6*6\xa9', 17 | 'pubkey_hex' : { 18 | 'compressed' : '03b805174bd496b275e711d5a9f1bcbaa4bba1a77176dbdb5fdd8b769da62a36a9', 19 | 'uncompressed': '04b805174bd496b275e711d5a9f1bcbaa4bba1a77176dbdb5fdd8b769da62a36a9c3dfa7c8ccb509f9a66efd6d8d1db6b25aa7c100476154b6303d76c28eda099b', 20 | }, 21 | 'address': { 22 | 'live_compressed' : '1N8FRuC7P1ZtLfkjrvGrCTGkZXuLk4p8rE', 23 | 'live_uncompressed': '1MGu43MAwpDnKb4d3xmNZvupLwk6iaaQay', 24 | 'test_compressed' : 'n2eCixH6C3197nEMaVFE2NV5RXW3cazW4Q', 25 | 'test_uncompressed': 'n1nrM6S9kqf36hYEmXjkPr89CwLobCH2nR', 26 | } 27 | } 28 | 29 | 30 | class TestPublicKey: 31 | 32 | def test_invalid_pair(self): 33 | with raises(PublicKey.InvalidPair): 34 | PublicKey((0, 0)) 35 | 36 | 37 | def test_unknown_network(self): 38 | with raises(PublicKey.UnknownNetwork): 39 | PublicKey(data['pubkey_pair'], network = 'a') 40 | 41 | 42 | def test_from_private_key(self): 43 | privkey = PrivateKey.from_hex(data['privkey_hex']) 44 | pubkey = PublicKey.from_private_key(privkey) 45 | 46 | assert pubkey.network is privkey.network 47 | assert pubkey.compressed == privkey.compressed 48 | assert pubkey.pair == data['pubkey_pair'] 49 | 50 | 51 | def test_from_hex_compressed(self): 52 | pubkey = PublicKey.from_hex(data['pubkey_hex']['compressed']) 53 | 54 | assert pubkey.pair == data['pubkey_pair'] 55 | assert pubkey.compressed is True 56 | assert pubkey.network is bitforge.networks.default 57 | 58 | 59 | def test_to_hex_compressed(self): 60 | string = data['pubkey_hex']['compressed'] 61 | assert PublicKey.from_hex(string).to_hex() == string 62 | 63 | 64 | def test_from_hex_uncompressed(self): 65 | pubkey = PublicKey.from_hex(data['pubkey_hex']['uncompressed']) 66 | 67 | assert pubkey.pair == data['pubkey_pair'] 68 | assert pubkey.compressed is False 69 | assert pubkey.network is bitforge.networks.default 70 | 71 | 72 | def test_from_hex_errors(self): 73 | with raises(PublicKey.InvalidHex): PublicKey.from_hex('a') 74 | with raises(PublicKey.InvalidHex): PublicKey.from_hex('a@') 75 | 76 | 77 | def test_to_hex_uncompressed(self): 78 | string = data['pubkey_hex']['uncompressed'] 79 | assert PublicKey.from_hex(string).to_hex() == string 80 | 81 | 82 | def test_from_bytes(self): 83 | pubkey = PublicKey.from_bytes(data['pubkey_bin']) 84 | 85 | assert pubkey.pair == data['pubkey_pair'] 86 | assert pubkey.compressed is True 87 | assert pubkey.network is bitforge.networks.default 88 | 89 | with raises(PublicKey.InvalidBinary): 90 | PublicKey.from_bytes(b'a') 91 | 92 | with raises(PublicKey.InvalidBinary): 93 | PublicKey.from_bytes(b'a' * 70) 94 | 95 | 96 | def test_to_bytes(self): 97 | bytes = data['pubkey_bin'] 98 | assert PublicKey.from_bytes(bytes).to_bytes() == bytes 99 | 100 | 101 | def test_to_address_live_compress(self): 102 | pubkey = PublicKey.from_hex(data['pubkey_hex']['compressed'], bitforge.networks.livenet) 103 | assert pubkey.to_address().to_string() == data['address']['live_compressed'] 104 | 105 | 106 | def test_to_address_live_uncompress(self): 107 | pubkey = PublicKey.from_hex(data['pubkey_hex']['uncompressed'], bitforge.networks.livenet) 108 | assert pubkey.to_address().to_string() == data['address']['live_uncompressed'] 109 | 110 | 111 | def test_to_address_test_compress(self): 112 | pubkey = PublicKey.from_hex(data['pubkey_hex']['compressed'], bitforge.networks.testnet) 113 | assert pubkey.to_address().to_string() == data['address']['test_compressed'] 114 | 115 | 116 | def test_to_address_test_uncompress(self): 117 | pubkey = PublicKey.from_hex(data['pubkey_hex']['uncompressed'], bitforge.networks.testnet) 118 | assert pubkey.to_address().to_string() == data['address']['test_uncompressed'] 119 | -------------------------------------------------------------------------------- /tests/test_transaction.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from bitforge import PrivateKey 4 | from bitforge import Transaction, Input, Output, Script 5 | from bitforge.transaction import AddressOutput, ScriptOutput, DataOutput 6 | from bitforge.transaction import AddressInput, ScriptInput, MultisigInput 7 | from bitforge.script import PayToPubkeyOut, PayToScriptOut, OpReturnOut 8 | from bitforge.script import PayToPubkeyIn, PayToScriptIn 9 | from bitforge.script.opcode import OP_0 10 | 11 | 12 | class MockInput(Input): 13 | def __new__(cls): 14 | return super(MockInput, cls).__new__(cls, '', 0, Script()) 15 | 16 | class MockOutput(Output): 17 | def __new__(cls): 18 | return super(MockOutput, cls).__new__(cls, 1000, Script()) 19 | 20 | class MockTransaction(Transaction): 21 | def __new__(cls, inputs = [ MockInput() ], outputs = [ MockOutput() ], lock_time = 0, version = 1): 22 | return super(MockTransaction, cls).__new__(cls, inputs, outputs, lock_time, version) 23 | 24 | 25 | class TestTransaction: 26 | def test_create(self): 27 | MockTransaction() 28 | 29 | def test_no_inputs(self): 30 | with raises(Transaction.NoInputs): 31 | Transaction([], []) 32 | 33 | def test_no_outputs(self): 34 | with raises(Transaction.NoOutputs): 35 | Transaction([ MockInput() ], []) 36 | 37 | def test_invalid_lock_time(self): 38 | with raises(Transaction.InvalidLockTime): 39 | Transaction([ MockInput() ], [ MockOutput() ], -1) 40 | 41 | 42 | class TestInput: 43 | def test_create(self): 44 | MockInput() 45 | 46 | def test_classify(self): 47 | privkey = PrivateKey() 48 | pubkey = privkey.to_public_key() 49 | address = pubkey.to_address() 50 | signature = b'foo' 51 | script = Script.compile([ OP_0 ]) 52 | 53 | i_addr = Input.create('1' * 32, 0, PayToPubkeyIn.create(address, signature)) 54 | assert isinstance(i_addr, AddressInput) 55 | 56 | i_script = Input.create('1' * 32, 0, PayToScriptIn.create(script, [signature])) 57 | assert isinstance(i_script, ScriptInput) 58 | 59 | def test_can_sign_address(self): 60 | privkey = PrivateKey() 61 | pubkey = privkey.to_public_key() 62 | address = pubkey.to_address() 63 | 64 | i_addr = AddressInput.create('a', 0, address) 65 | 66 | assert i_addr.can_sign([ privkey ]) 67 | assert not i_addr.can_sign([ PrivateKey() ]) 68 | 69 | def test_can_sign_multisig(self): 70 | privkeys = [ PrivateKey(), PrivateKey() ] 71 | pubkeys = [ privkey.to_public_key() for privkey in privkeys ] 72 | 73 | i_addr = MultisigInput.create('a', 0, pubkeys, 1) 74 | 75 | assert i_addr.can_sign([ privkeys[0] ]) 76 | assert i_addr.can_sign([ privkeys[1] ]) 77 | 78 | assert not i_addr.can_sign([ PrivateKey() ]) 79 | assert not i_addr.can_sign([ PrivateKey(), PrivateKey() ]) 80 | assert not i_addr.can_sign([ PrivateKey(), privkeys[1] ]) 81 | 82 | 83 | class TestOutput: 84 | def test_create(self): 85 | MockOutput() 86 | 87 | def test_too_much_data(self): 88 | with raises(DataOutput.TooMuchData): 89 | DataOutput.create(b'0' * 81) 90 | 91 | def test_classify(self): 92 | privkey = PrivateKey() 93 | pubkey = privkey.to_public_key() 94 | address = pubkey.to_address() 95 | script = Script() 96 | 97 | o_addr = Output.create(1, PayToPubkeyOut.create(address)) 98 | assert isinstance(o_addr, AddressOutput) 99 | 100 | o_script = Output.create(1, PayToScriptOut.create(script)) 101 | assert isinstance(o_script, ScriptOutput) 102 | 103 | o_data = Output.create(1, OpReturnOut.create(b'data')) 104 | assert isinstance(o_data, DataOutput) 105 | -------------------------------------------------------------------------------- /tests/test_unit.py: -------------------------------------------------------------------------------- 1 | from bitforge.unit import Unit 2 | 3 | 4 | class TestUnit: 5 | 6 | def test_btc_accessors(self): 7 | u = Unit(btc = 1.2) 8 | assert u.btc == 1.2 9 | assert u.mbtc == 1200 10 | assert u.bits == 1200000 11 | assert u.satoshis == 120000000 12 | 13 | def test_btc_conversion(self): 14 | u = Unit(btc = 1.3) 15 | assert u.mbtc == 1300 16 | assert u.bits == 1300000 17 | assert u.satoshis == 130000000 18 | 19 | u = Unit(mbtc = 1.3) 20 | assert u.btc == 0.0013 21 | assert u.bits == 1300 22 | assert u.satoshis == 130000 23 | 24 | u = Unit(bits = 1.3) 25 | assert u.btc == 0.0000013 26 | assert u.mbtc == 0.0013 27 | assert u.satoshis == 130 28 | 29 | u = Unit(satoshis = 3) 30 | assert u.btc == 0.00000003 31 | assert u.mbtc == 0.00003 32 | assert u.bits == 0.03 33 | 34 | # TODO: Review presition 35 | # def test_unit_rates(self): 36 | # u = Unit.from_fiat(1.3, 350) 37 | # assert u.at_rate(350) == 1.3 38 | 39 | # u = Unit(btc = 0.0123) 40 | # assert u.at_rate(10) == 0.12 41 | 42 | def test_repr(self): 43 | u = Unit(btc = 1.3) 44 | assert repr(u) == '' 45 | -------------------------------------------------------------------------------- /tests/test_uri.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from bitforge import URI 4 | from bitforge import Address 5 | from bitforge import Unit 6 | 7 | import bitforge.networks 8 | 9 | class TestURI: 10 | 11 | def test_parse_uri(self): 12 | uri = URI.parse('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 13 | assert uri['address'] == u'1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj' 14 | 15 | def test_parse_uri_amount(self): 16 | uri = URI.parse('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=123.22') 17 | assert uri['address'] == u'1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj' 18 | assert uri['amount'] == u'123.22' 19 | 20 | def test_uri_extras(self): 21 | uri = URI.parse('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=123.22&other-param=something&req-extra=param') 22 | assert uri['address'] == '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj' 23 | assert uri['amount'] == '123.22' 24 | assert uri['other-param'] == u'something' 25 | assert uri['req-extra'] == u'param' 26 | 27 | def test_is_valid(self): 28 | assert URI.is_valid('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 29 | assert URI.is_valid('bitcoin:mkYY5NRvikVBY1EPtaq9fAFgquesdjqECw') 30 | 31 | assert URI.is_valid('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=1.2') 32 | assert URI.is_valid('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=1.2&other=param') 33 | assert URI.is_valid('bitcoin:mmrqEBJxUCf42vdb3oozZtyz5mKr3Vb2Em?amount=0.1&r=https%3A%2F%2Ftest.bitpay.com%2Fi%2F6DKgf8cnJC388irbXk5hHu') 34 | 35 | assert not URI.is_valid('bitcoin:') 36 | assert not URI.is_valid('bitcoin:badUri') 37 | assert not URI.is_valid('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfk?amount=bad') 38 | assert not URI.is_valid('bitcoin:?r=https%3A%2F%2Ftest.bitpay.com%2Fi%2F6DKgf8cnJC388irbXk5hHu') 39 | 40 | def test_uri_address(self): 41 | uri = URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 42 | assert isinstance(uri.address, Address) 43 | assert uri.address.network == bitforge.networks.livenet 44 | 45 | uri = URI('bitcoin:mkYY5NRvikVBY1EPtaq9fAFgquesdjqECw') 46 | assert isinstance(uri.address, Address) 47 | assert uri.address.network == bitforge.networks.testnet 48 | 49 | def test_uri_amount(self): 50 | uri = URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=123.22') 51 | assert isinstance(uri.amount, Unit) 52 | assert uri.amount.satoshis == 12322000000 53 | 54 | def test_uri_extras(self): 55 | uri = URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=1.2&other=param') 56 | assert uri.extras['other'] == u'param' 57 | 58 | def test_create_params(self): 59 | uri = URI({ 60 | 'address': '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj', 61 | 'amount' : 120000000, 62 | 'other' : 'param' 63 | }) 64 | 65 | assert isinstance(uri.address, Address) 66 | assert isinstance(uri.amount, Unit) and uri.amount.satoshis == 120000000 67 | assert uri.extras['other'] == u'param' 68 | 69 | def test_create_required(self): 70 | uri = URI({ 71 | 'address' : '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj', 72 | 'req-other': 'param' 73 | }) 74 | 75 | assert isinstance(uri.address, Address) 76 | assert uri.extras['req-other'] == u'param' 77 | 78 | def test_parse_support(self): 79 | uri = URI('bitcoin://1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 80 | assert uri.address.to_string() == '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj' 81 | 82 | def test_str_roundtrip(self): 83 | uri = 'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?label=myLabel&message=Donation+for+project+xyz&other=xD' 84 | assert URI(uri).to_uri() == uri 85 | 86 | def test_support_url_ecoded(self): 87 | uri = URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?message=Donation+for+project+xyz&other=xD&label=myLabel') 88 | assert uri.message == u'Donation for project xyz' 89 | assert uri.label == u'myLabel' 90 | assert uri.extras['other'] == u'xD' 91 | 92 | def test_str(self): 93 | uri = URI({'address': '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj'}) 94 | assert uri.to_uri() == 'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj' 95 | 96 | uri = URI({ 97 | 'address': '1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj', 98 | 'amount': 110001000, 99 | 'message': 'Hello World', 100 | 'something': 'else' 101 | }) 102 | 103 | assert uri.to_uri() == 'bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj?amount=1.10001&message=Hello+World&something=else' 104 | 105 | def test_protocol_case_insensitive(self): 106 | uri1 = URI('bItcOin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 107 | uri2 = URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 108 | assert uri1.to_uri() == uri2.to_uri() 109 | 110 | def test_encondes_r_correctly(self): 111 | uri = 'bitcoin:mmrqEBJxUCf42vdb3oozZtyz5mKr3Vb2Em?amount=0.1&r=https%3A%2F%2Ftest.bitpay.com%2Fi%2F6DKgf8cnJC388irbXk5hHu' 112 | assert URI(uri).to_uri() == uri 113 | 114 | def test_repr(self): 115 | uri = URI('bitcoin:1DP69gMMvSuYhbnxsi4EJEFufUAbDrEQfj') 116 | assert repr(uri) == '' 117 | --------------------------------------------------------------------------------