├── docs ├── .nojekyll ├── _static │ ├── custom.css │ ├── file.png │ ├── minus.png │ ├── plus.png │ ├── documentation_options.js │ └── pygments.css ├── objects.inv ├── README.md ├── source │ ├── tx.rst │ ├── txin.rst │ ├── utils.rst │ ├── txout.rst │ ├── outpoint.rst │ ├── witnessstackitem.rst │ ├── bytedata.rst │ ├── simple.rst │ ├── networks.rst │ ├── tx builder.rst │ ├── encoders.rst │ ├── script.rst │ ├── inputwitness.rst │ ├── transactions.rst │ ├── encoding.rst │ ├── index.rst │ └── conf.py ├── _sources │ ├── tx.rst.txt │ ├── txin.rst.txt │ ├── utils.rst.txt │ ├── txout.rst.txt │ ├── outpoint.rst.txt │ ├── txbuilder.rst.txt │ ├── witnessstackitem.rst.txt │ ├── bytedata.rst.txt │ ├── simple.rst.txt │ ├── networks.rst.txt │ ├── tx builder.rst.txt │ ├── encoders.rst.txt │ ├── script.rst.txt │ ├── inputwitness.rst.txt │ ├── transactions.rst.txt │ ├── encoding.rst.txt │ └── index.rst.txt ├── .buildinfo ├── Makefile ├── make.bat ├── txbuilder.html ├── search.html ├── py-modindex.html ├── witnessstackitem.html ├── transactions.html ├── txout.html ├── inputwitness.html ├── index.html └── outpoint.html ├── riemann ├── py.typed ├── encoding │ ├── __init__.py │ ├── cashaddr.py │ ├── base58.py │ └── bech32.py ├── tests │ ├── __init__.py │ ├── tx │ │ ├── __init__.py │ │ ├── helpers │ │ │ └── __init__.py │ │ ├── test_overwinter_sighash.py │ │ ├── test_overwinter.py │ │ ├── test_shared.py │ │ └── test_tx_builder.py │ ├── encoding │ │ ├── __init__.py │ │ ├── test_base58.py │ │ ├── test_bech32.py │ │ └── test_addresses.py │ ├── networks │ │ └── test_networks.py │ ├── test_riemann.py │ ├── script │ │ ├── SPQR.py │ │ └── test_serialization.py │ ├── test_simple.py │ └── test_blake256.py ├── script │ ├── __init__.py │ ├── examples.py │ ├── opcodes.py │ └── serialization.py ├── tx │ ├── __init__.py │ ├── zcash_shared.py │ └── shared.py ├── examples │ ├── prep_tx_for_signing.py │ ├── sapling_tx_parsing.py │ ├── sapling_example.py │ ├── sapling_htlc_example.py │ └── p2pkh_op_return.py ├── __init__.py ├── networks │ ├── __init__.py │ └── standard.py ├── simple.py └── utils.py ├── requirements-test.txt ├── setup.cfg ├── .coveragerc ├── .gitignore ├── .travis.yml ├── LICENSE-APACHE2.md ├── tox.ini ├── setup.py ├── LICENSE-MIT.md ├── CONTRIBUTING.md └── README.md /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/encoding/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/tests/tx/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/tests/encoding/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/tests/tx/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /riemann/tests/tx/test_overwinter_sighash.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | /* This file intentionally left blank. */ 2 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | pip==18.0 2 | flake8 3 | pytest 4 | pytest-cov 5 | tox 6 | -------------------------------------------------------------------------------- /riemann/script/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from .serialization import * 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | license = LICENSE 4 | -------------------------------------------------------------------------------- /docs/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summa-tx/riemann/HEAD/docs/objects.inv -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Contributing to documentation 2 | 3 | Modify the files in `source`. 4 | -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summa-tx/riemann/HEAD/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summa-tx/riemann/HEAD/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/summa-tx/riemann/HEAD/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/source/tx.rst: -------------------------------------------------------------------------------- 1 | Tx 2 | == 3 | .. autoclass:: riemann.tx.Tx 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/tx.rst.txt: -------------------------------------------------------------------------------- 1 | Tx 2 | == 3 | .. autoclass:: riemann.tx.Tx 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/source/txin.rst: -------------------------------------------------------------------------------- 1 | TxIn 2 | ==== 3 | .. autoclass:: riemann.tx.TxIn 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/source/utils.rst: -------------------------------------------------------------------------------- 1 | Utils 2 | ===== 3 | .. automodule:: riemann.utils 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/txin.rst.txt: -------------------------------------------------------------------------------- 1 | TxIn 2 | ==== 3 | .. autoclass:: riemann.tx.TxIn 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/utils.rst.txt: -------------------------------------------------------------------------------- 1 | Utils 2 | ===== 3 | .. automodule:: riemann.utils 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/source/txout.rst: -------------------------------------------------------------------------------- 1 | TxOut 2 | ===== 3 | .. autoclass:: riemann.tx.TxOut 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/txout.rst.txt: -------------------------------------------------------------------------------- 1 | TxOut 2 | ===== 3 | .. autoclass:: riemann.tx.TxOut 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/source/outpoint.rst: -------------------------------------------------------------------------------- 1 | Outpoint 2 | ======== 3 | .. autoclass:: riemann.tx.Outpoint 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/outpoint.rst.txt: -------------------------------------------------------------------------------- 1 | Outpoint 2 | ======== 3 | .. autoclass:: riemann.tx.Outpoint 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/txbuilder.rst.txt: -------------------------------------------------------------------------------- 1 | Tx Builder 2 | ========== 3 | 4 | .. automodule:: riemann.script.tx_builder 5 | :members: 6 | :undoc-members: 7 | -------------------------------------------------------------------------------- /docs/source/witnessstackitem.rst: -------------------------------------------------------------------------------- 1 | WitnessStackItem 2 | ================ 3 | .. autoclass:: riemann.tx.WitnessStackItem 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/_sources/witnessstackitem.rst.txt: -------------------------------------------------------------------------------- 1 | WitnessStackItem 2 | ================ 3 | .. autoclass:: riemann.tx.WitnessStackItem 4 | :members: 5 | :undoc-members: 6 | -------------------------------------------------------------------------------- /docs/source/bytedata.rst: -------------------------------------------------------------------------------- 1 | ByteData 2 | ================== 3 | 4 | All other transaction components inherit from this class. 5 | 6 | .. autoclass:: riemann.tx.ByteData 7 | :members: 8 | :undoc-members: 9 | -------------------------------------------------------------------------------- /docs/_sources/bytedata.rst.txt: -------------------------------------------------------------------------------- 1 | ByteData 2 | ================== 3 | 4 | All other transaction components inherit from this class. 5 | 6 | .. autoclass:: riemann.tx.ByteData 7 | :members: 8 | :undoc-members: 9 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = riemann/ 3 | omit = 4 | riemann/tests/* 5 | riemann/blake256.py 6 | riemann/blake2.py 7 | 8 | [report] 9 | show_missing = True 10 | sort = Miss 11 | 12 | [html] 13 | directory = coverage/html/ 14 | -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 5fdb83a35db2b25ffa1e7ee044cb2ea2 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .mypy_cache/ 2 | *.pyc 3 | *.egg-info/ 4 | build/ 5 | dist/ 6 | MANIFEST.in 7 | 8 | # Virtual environment 9 | venv/ 10 | .venv/ 11 | 12 | # Unit test / coverage reports 13 | .tox/ 14 | coverage/ 15 | .coverage 16 | .cache 17 | .pytest_cache 18 | -------------------------------------------------------------------------------- /docs/source/simple.rst: -------------------------------------------------------------------------------- 1 | Simple 2 | ====== 3 | 4 | Riemann offers a simplified transaction builder that accepts human-readable 5 | inputs, i.e. integers and strings instead of serialized LE numbers and 6 | bytestrings. 7 | 8 | .. automodule:: riemann.simple 9 | :members: 10 | :undoc-members: 11 | -------------------------------------------------------------------------------- /docs/_sources/simple.rst.txt: -------------------------------------------------------------------------------- 1 | Simple 2 | ====== 3 | 4 | Riemann offers a simplified transaction builder that accepts human-readable 5 | inputs, i.e. integers and strings instead of serialized LE numbers and 6 | bytestrings. 7 | 8 | .. automodule:: riemann.simple 9 | :members: 10 | :undoc-members: 11 | -------------------------------------------------------------------------------- /riemann/tx/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | from riemann.tx.tx import * 3 | from riemann.tx.shared import * 4 | from riemann.tx.decred import * 5 | from riemann.tx.sprout import * 6 | from riemann.tx.sapling import * 7 | from riemann.tx.overwinter import * 8 | from riemann.tx.tx_builder import * # type: ignore 9 | from riemann.tx.zcash_shared import * 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | matrix: 4 | include: 5 | - python: 3.6 6 | - python: 3.7 7 | dist: xenial 8 | sudo: true 9 | install: 10 | - pip install tox 11 | - pip install tox-travis 12 | 13 | script: 14 | - tox 15 | 16 | after_success: 17 | - pip install python-coveralls 18 | - coveralls 19 | -------------------------------------------------------------------------------- /docs/_static/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: 'v2.0.3', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | FILE_SUFFIX: '.html', 7 | HAS_SOURCE: true, 8 | SOURCELINK_SUFFIX: '.txt', 9 | NAVIGATION_WITH_KEYS: false 10 | }; -------------------------------------------------------------------------------- /docs/source/networks.rst: -------------------------------------------------------------------------------- 1 | Networks 2 | ======== 3 | 4 | Riemann defaults to Bitcoin mainnet, but can be set to use many different UTXO 5 | networks. Not all are throughly tested! 6 | 7 | .. automodule:: riemann 8 | :members: 9 | :undoc-members: 10 | 11 | Supported Networks 12 | ------------------ 13 | 14 | .. automodule:: riemann.networks.networks 15 | :members: 16 | :undoc-members: 17 | -------------------------------------------------------------------------------- /docs/_sources/networks.rst.txt: -------------------------------------------------------------------------------- 1 | Networks 2 | ======== 3 | 4 | Riemann defaults to Bitcoin mainnet, but can be set to use many different UTXO 5 | networks. Not all are throughly tested! 6 | 7 | .. automodule:: riemann 8 | :members: 9 | :undoc-members: 10 | 11 | Supported Networks 12 | ------------------ 13 | 14 | .. automodule:: riemann.networks.networks 15 | :members: 16 | :undoc-members: 17 | -------------------------------------------------------------------------------- /docs/source/tx builder.rst: -------------------------------------------------------------------------------- 1 | Tx Builder 2 | ========== 3 | 4 | `tx_builder` is a collection of useful functions for instantiating transactions 5 | or components of transactions. As opposed to :ref:`simple`, `tx_builder` offers 6 | more low-level control, and accepts instantiated or components rather than 7 | human-readable ones. 8 | 9 | .. automodule:: riemann.tx.tx_builder 10 | :members: 11 | :undoc-members: 12 | -------------------------------------------------------------------------------- /docs/_sources/tx builder.rst.txt: -------------------------------------------------------------------------------- 1 | Tx Builder 2 | ========== 3 | 4 | `tx_builder` is a collection of useful functions for instantiating transactions 5 | or components of transactions. As opposed to :ref:`simple`, `tx_builder` offers 6 | more low-level control, and accepts instantiated or components rather than 7 | human-readable ones. 8 | 9 | .. automodule:: riemann.tx.tx_builder 10 | :members: 11 | :undoc-members: 12 | -------------------------------------------------------------------------------- /docs/source/encoders.rst: -------------------------------------------------------------------------------- 1 | Encoders 2 | ======== 3 | 4 | Riemann offers 3 encoders: `base58`, `bech32`, and `cashaddr` (also bech32). 5 | Each exposes an `encode` and `decode` function. 6 | 7 | 8 | Base58 9 | ------ 10 | 11 | .. automodule:: riemann.encoding.base58 12 | :members: 13 | :undoc-members: 14 | 15 | Bech32 16 | ------ 17 | 18 | .. automodule:: riemann.encoding.bech32 19 | :members: 20 | :undoc-members: 21 | 22 | Cashaddr 23 | -------- 24 | 25 | .. automodule:: riemann.encoding.cashaddr 26 | :members: 27 | :undoc-members: 28 | -------------------------------------------------------------------------------- /docs/_sources/encoders.rst.txt: -------------------------------------------------------------------------------- 1 | Encoders 2 | ======== 3 | 4 | Riemann offers 3 encoders: `base58`, `bech32`, and `cashaddr` (also bech32). 5 | Each exposes an `encode` and `decode` function. 6 | 7 | 8 | Base58 9 | ------ 10 | 11 | .. automodule:: riemann.encoding.base58 12 | :members: 13 | :undoc-members: 14 | 15 | Bech32 16 | ------ 17 | 18 | .. automodule:: riemann.encoding.bech32 19 | :members: 20 | :undoc-members: 21 | 22 | Cashaddr 23 | -------- 24 | 25 | .. automodule:: riemann.encoding.cashaddr 26 | :members: 27 | :undoc-members: 28 | -------------------------------------------------------------------------------- /riemann/examples/prep_tx_for_signing.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from riemann import simple 4 | 5 | # Spend a P2PKH output: 6 | tx_ins = [simple.unsigned_input(outpoint)] 7 | 8 | # Spend a P2SH output 9 | tx_ins += [simple.unsigned_input( 10 | outpoint=outpoint, 11 | redeem_script=redeem_script)] 12 | 13 | # Make an output 14 | tx_outs = [simple.output(value, address)] 15 | 16 | # Build the transaction 17 | tx = simple.unsigned_tx(tx_ins, tx_outs) 18 | 19 | sighash_0 = tx.sighash_single(0, p2pkh_pk_script) 20 | sighash_1 tx.sighash_all(1, redeem_script, anyone_can_pay=True) 21 | -------------------------------------------------------------------------------- /LICENSE-APACHE2.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Indefinite Integral Incorporated 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /riemann/tests/networks/test_networks.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import riemann.networks as networks 3 | 4 | 5 | class TestNetworks(unittest.TestCase): 6 | 7 | def setUp(self): 8 | pass 9 | 10 | def test_get_network(self): 11 | for name in networks.SUPPORTED: 12 | n = networks.get_network(name) 13 | self.assertEqual(n, networks.SUPPORTED[name]) 14 | 15 | # Test Error case 16 | with self.assertRaises(ValueError) as context: 17 | networks.get_network('toast') 18 | 19 | self.assertIn('Unknown chain specifed: {}'.format('toast'), 20 | str(context.exception)) 21 | -------------------------------------------------------------------------------- /docs/source/script.rst: -------------------------------------------------------------------------------- 1 | Script 2 | ====== 3 | 4 | Riemann provides support for (de)serializing Bitcoin Script. However, it does 5 | not support execution of Script. 6 | 7 | In Riemann's Script serialization, opcodes are represented as strings and data 8 | pushes are represented as unprefixed hex strings. E.g. `"0011aabb OP_HASH160"`. 9 | This can be serialized to bytes via `serialization.serialize` or to hex via 10 | `serialization.hex_serialize`. 11 | 12 | Some examples are available in `riemann/script/examples.py` and 13 | `riemann/examples`. 14 | 15 | Serialization 16 | ------------- 17 | 18 | .. automodule:: riemann.script.serialization 19 | :members: 20 | :undoc-members: 21 | -------------------------------------------------------------------------------- /docs/_sources/script.rst.txt: -------------------------------------------------------------------------------- 1 | Script 2 | ====== 3 | 4 | Riemann provides support for (de)serializing Bitcoin Script. However, it does 5 | not support execution of Script. 6 | 7 | In Riemann's Script serialization, opcodes are represented as strings and data 8 | pushes are represented as unprefixed hex strings. E.g. `"0011aabb OP_HASH160"`. 9 | This can be serialized to bytes via `serialization.serialize` or to hex via 10 | `serialization.hex_serialize`. 11 | 12 | Some examples are available in `riemann/script/examples.py` and 13 | `riemann/examples`. 14 | 15 | Serialization 16 | ------------- 17 | 18 | .. automodule:: riemann.script.serialization 19 | :members: 20 | :undoc-members: 21 | -------------------------------------------------------------------------------- /docs/source/inputwitness.rst: -------------------------------------------------------------------------------- 1 | InputWitness 2 | ============ 3 | 4 | An `InputWitness` is the SegWit datastructure containing spend authorization 5 | for a specific input. It consists of a tuple (the `stack`) of 6 | :ref:`WitnessStackItem` objects. 7 | 8 | Within a `Tx` the list of `InputWitness` objects is called `tx_witnesses`. Each 9 | witness and each item in its stack can be indexed by its position, e.g. 10 | `my_tx.tx_witnesses[0].stack[4]`. In the case of p2wsh inputs, the last stack 11 | item `my_input_witness.stack[-1]` is called the Witness Script, and contains a 12 | serialized Script program. 13 | 14 | .. autoclass:: riemann.tx.InputWitness 15 | :members: 16 | :undoc-members: 17 | -------------------------------------------------------------------------------- /docs/_sources/inputwitness.rst.txt: -------------------------------------------------------------------------------- 1 | InputWitness 2 | ============ 3 | 4 | An `InputWitness` is the SegWit datastructure containing spend authorization 5 | for a specific input. It consists of a tuple (the `stack`) of 6 | :ref:`WitnessStackItem` objects. 7 | 8 | Within a `Tx` the list of `InputWitness` objects is called `tx_witnesses`. Each 9 | witness and each item in its stack can be indexed by its position, e.g. 10 | `my_tx.tx_witnesses[0].stack[4]`. In the case of p2wsh inputs, the last stack 11 | item `my_input_witness.stack[-1]` is called the Witness Script, and contains a 12 | serialized Script program. 13 | 14 | .. autoclass:: riemann.tx.InputWitness 15 | :members: 16 | :undoc-members: 17 | -------------------------------------------------------------------------------- /riemann/__init__.py: -------------------------------------------------------------------------------- 1 | from riemann import networks 2 | 3 | network = networks.get_network('bitcoin_main') 4 | 5 | 6 | def select_network(name: str): 7 | ''' 8 | Set the library to use a supported network 9 | 10 | Examples: 11 | 12 | - bitcon_main 13 | - litecoin_test 14 | - zcash_sapling_main 15 | ''' 16 | global network 17 | network = networks.get_network(name) 18 | 19 | 20 | def get_current_network() -> networks.Network: 21 | '''Return the current network as a class''' 22 | return network 23 | 24 | 25 | def get_current_network_name() -> str: 26 | '''Return the name of the current network''' 27 | return '{}_{}'.format(network.NETWORK_NAME, network.SUBNET_NAME) 28 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | github: 18 | @make html 19 | @cp -a build/html/. ../docs 20 | 21 | # Catch-all target: route all unknown targets to Sphinx using the new 22 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 23 | %: Makefile 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | -------------------------------------------------------------------------------- /docs/source/transactions.rst: -------------------------------------------------------------------------------- 1 | Riemann Transactions 2 | ==================== 3 | 4 | Riemann supports transactions in many chains. See :ref:`Networks` for a 5 | complete list. 6 | 7 | Riemann represents transactions as bytestrings with some additional sugar on 8 | top. All elements of a transaction inherit from the `ByteData` class. The 9 | ByteData class provides useful syntactic sugar: 10 | 11 | .. code-block:: python 12 | 13 | # indexing 14 | version = my_tx[:4] 15 | 16 | # formatting 17 | f'{my_tx:x}' 18 | 19 | # equality 20 | my_tx == some_other_tx 21 | 22 | 23 | 24 | Generally, developers want to interact with the `Tx` class and its components, 25 | `TxIn`, `TxOut`, and `InputWitness`. 26 | 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | :caption: Tx Classes: 31 | 32 | bytedata 33 | outpoint 34 | txin 35 | txout 36 | witnessstackitem 37 | inputwitness 38 | tx 39 | -------------------------------------------------------------------------------- /docs/_sources/transactions.rst.txt: -------------------------------------------------------------------------------- 1 | Riemann Transactions 2 | ==================== 3 | 4 | Riemann supports transactions in many chains. See :ref:`Networks` for a 5 | complete list. 6 | 7 | Riemann represents transactions as bytestrings with some additional sugar on 8 | top. All elements of a transaction inherit from the `ByteData` class. The 9 | ByteData class provides useful syntactic sugar: 10 | 11 | .. code-block:: python 12 | 13 | # indexing 14 | version = my_tx[:4] 15 | 16 | # formatting 17 | f'{my_tx:x}' 18 | 19 | # equality 20 | my_tx == some_other_tx 21 | 22 | 23 | 24 | Generally, developers want to interact with the `Tx` class and its components, 25 | `TxIn`, `TxOut`, and `InputWitness`. 26 | 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | :caption: Tx Classes: 31 | 32 | bytedata 33 | outpoint 34 | txin 35 | txout 36 | witnessstackitem 37 | inputwitness 38 | tx 39 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | cov-init 4 | py36 5 | py37 6 | cov-report 7 | 8 | [pytest] 9 | nonrecursedirs = .git .tox venv coverage 10 | 11 | [testenv] 12 | usedevelop=True 13 | setenv = 14 | COVERAGE_FILE = .coverage.{envname} 15 | deps = -rrequirements-test.txt 16 | install_command = pip install {opts} {packages} 17 | 18 | commands = 19 | flake8 \ 20 | --ignore=W503 \ 21 | --exclude test_tx.py \ 22 | riemann 23 | pytest \ 24 | -q \ 25 | --cov-config .coveragerc \ 26 | --cov-report= \ 27 | --cov 28 | 29 | [testenv:cov-init] 30 | basepython = python3.7 31 | setenv = 32 | COVERAGE_FILE = .coverage 33 | deps = coverage 34 | commands = 35 | coverage erase 36 | 37 | [testenv:cov-report] 38 | basepython = python3.7 39 | setenv = 40 | COVERAGE_FILE = .coverage 41 | deps = coverage 42 | commands = 43 | coverage combine 44 | coverage report 45 | coverage html 46 | -------------------------------------------------------------------------------- /riemann/tests/test_riemann.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import riemann 3 | from riemann import networks 4 | 5 | 6 | class test_riemann(unittest.TestCase): 7 | 8 | def setUp(self): 9 | pass 10 | 11 | def test_select_network(self): 12 | for n in networks.SUPPORTED: 13 | riemann.select_network(n) 14 | self.assertIs(riemann.network, networks.SUPPORTED[n]) 15 | 16 | def test_get_current_network(self): 17 | for n in networks.SUPPORTED: 18 | riemann.select_network(n) 19 | self.assertIs(riemann.get_current_network(), 20 | networks.SUPPORTED[n]) 21 | 22 | def test_get_current_network_name(self): 23 | for n in networks.SUPPORTED: 24 | riemann.select_network(n) 25 | self.assertEqual(riemann.get_current_network_name(), n) 26 | 27 | def tearDown(self): 28 | riemann.select_network('bitcoin_main') 29 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /riemann/examples/sapling_tx_parsing.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | from riemann import tx 3 | 4 | from riemann import utils 5 | 6 | riemann.select_network('zcash_sapling_main') 7 | 8 | tx_hex = 'YOUR_TX_HEX_HERE' 9 | 10 | # Parse the transaction blob into an object 11 | sapling_tx = tx.SaplingTx.from_hex(tx_hex) 12 | 13 | # Interact with it easily 14 | print(utils.le2i(sapling_tx.expiry_height)) 15 | print(sapling_tx.tx_shielded_outputs[0].enc_ciphertext.hex()) 16 | print(sapling_tx.tx_joinsplits[0].zkproof.pi_sub_c.hex()) 17 | 18 | # Change some part of it 19 | new_tx = sapling_tx.copy(expiry_height=77) 20 | 21 | print(new_tx.hex()) 22 | 23 | # Calculate the sighash 24 | new_tx.sighash_all( 25 | joinsplit=False, # True if you're signing a joinsplit 26 | script_code=b'\x19\x76\x00\x88\xac', # the script code to sign 27 | anyone_can_pay=False, # Anyone can pay can't be used with JSs 28 | prevout_value=b'\x00' * 8) # little-endian 8 byte value in zatoshi 29 | -------------------------------------------------------------------------------- /docs/source/encoding.rst: -------------------------------------------------------------------------------- 1 | Encoding 2 | ======== 3 | 4 | Riemann provides support for encoding addresses for all supported chains, 5 | including native SegWit and cashaddrs. Typically, we recommend using the high 6 | level `addresses` module, but the encoders themselves are also available. 7 | 8 | We recommend generally using the `addresses` module. Addresses may be encoded 9 | by type (e.g. `make_p2wsh_address`) or you may allow Riemann to select for you 10 | (e.g. `make_sh_address`). Riemann prefers `cashaddr` where available, and 11 | legacy elsewhere. Developers wanting Segwit addresses should be careful to use 12 | Segwit specific methods. 13 | 14 | Note: Compatibility Segwit addresses (witness-over-p2sh) are not first-class 15 | address types in Riemann. You'll have to make script-sigs yourself. 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | :caption: Encoder Info: 20 | 21 | encoders 22 | 23 | 24 | Addresses 25 | --------- 26 | 27 | .. automodule:: riemann.encoding.addresses 28 | :members: 29 | :undoc-members: 30 | -------------------------------------------------------------------------------- /docs/_sources/encoding.rst.txt: -------------------------------------------------------------------------------- 1 | Encoding 2 | ======== 3 | 4 | Riemann provides support for encoding addresses for all supported chains, 5 | including native SegWit and cashaddrs. Typically, we recommend using the high 6 | level `addresses` module, but the encoders themselves are also available. 7 | 8 | We recommend generally using the `addresses` module. Addresses may be encoded 9 | by type (e.g. `make_p2wsh_address`) or you may allow Riemann to select for you 10 | (e.g. `make_sh_address`). Riemann prefers `cashaddr` where available, and 11 | legacy elsewhere. Developers wanting Segwit addresses should be careful to use 12 | Segwit specific methods. 13 | 14 | Note: Compatibility Segwit addresses (witness-over-p2sh) are not first-class 15 | address types in Riemann. You'll have to make script-sigs yourself. 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | :caption: Encoder Info: 20 | 21 | encoders 22 | 23 | 24 | Addresses 25 | --------- 26 | 27 | .. automodule:: riemann.encoding.addresses 28 | :members: 29 | :undoc-members: 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from setuptools import setup, find_packages 4 | 5 | setup( 6 | name='riemann-tx', 7 | version='2.1.0', 8 | description=('Transaction creation library for Bitcoin-like coins'), 9 | url='https://github.com/summa-tx/riemann', 10 | author='James Prestwich', 11 | author_email='james@summa.one', 12 | install_requires=[], 13 | packages=find_packages(), 14 | package_dir={'riemann': 'riemann'}, 15 | package_data={'riemann': ['py.typed']}, 16 | keywords = 'bitcoin litecoin cryptocurrency decred blockchain development', 17 | python_requires='>=3.6', 18 | classifiers = [ 19 | 'Programming Language :: Python', 20 | 'Programming Language :: Python :: 3 :: Only', 21 | 'Topic :: Security :: Cryptography', 22 | 'Development Status :: 4 - Beta', 23 | 'Intended Audience :: Developers', 24 | 'Operating System :: OS Independent', 25 | 'Topic :: Software Development :: Libraries :: Python Modules' 26 | ], 27 | license="MIT OR Apache-2.0" 28 | ) 29 | -------------------------------------------------------------------------------- /LICENSE-MIT.md: -------------------------------------------------------------------------------- 1 | Copyright 2020 Indefinite Integral Incorporated 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /riemann/script/examples.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # Needs two pubkeys 4 | msig_two_two = 'OP_2 {pk0} {pk1} OP_2 OP_CHECKMULTISIG' 5 | 6 | # Needs two signatures and the redeem_script 7 | msig_two_two_stack_script = 'OP_0 {sig0} {sig1}' 8 | 9 | # Needs signature and pubkey 10 | p2pkh_script_sig = '{sig} {pk}' 11 | 12 | # Needs one pubkey 13 | p2pkh_script_pubkey = 'OP_DUP OP_HASH160 {pk} OP_EQUALVERIFY OP_CHECKSIG' 14 | 15 | # Needs 20 byte scripthash 16 | p2sh_script_pubkey = 'OP_HASH160 {sh} OP_EQUAL' 17 | 18 | 19 | # Needs length-prefixed witness script 20 | p2w = 'OP_0 {}' 21 | 22 | # Needs a 32 byte hash, alice's pubkey, a timeout, and bob's pubkey 23 | htlc_redeem_script = \ 24 | 'OP_IF ' \ 25 | 'OP_SHA256 {secret_hash} OP_EQUALVERIFY ' \ 26 | 'OP_DUP OP_HASH160 {pkh0} ' \ 27 | 'OP_ELSE ' \ 28 | '{timeout} OP_CHECKLOCKTIMEVERIFY OP_DROP ' \ 29 | 'OP_DUP OP_HASH160 {pkh1} ' \ 30 | 'OP_ENDIF ' \ 31 | 'OP_EQUALVERIFY ' \ 32 | 'OP_CHECKSIG' 33 | 34 | # Needs (signature, pubkey, secret) 35 | # IN THAT ORDER! 36 | htlc_stack_script_execute = '{sig} {pk} {secret} OP_TRUE' 37 | 38 | # Needs (signature, pubkey, serialized_redeem_script) 39 | # IN THAT ORDER! 40 | htlc_stack_script_refund = '{sig} {pk} OP_FALSE' 41 | 42 | # Don't forget to attach the script hash to the end of the p2sh script sigs 43 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Riemann: Cryptocurrency Transactions for Humans 2 | =============================================== 3 | 4 | .. code-block:: bash 5 | 6 | $ pip install riemann-tx 7 | 8 | Riemann is a **dependency-free Python3** library for creating **bitcoin-style 9 | transactions**. It is **compatible with many chains** and **supports SegWit**. 10 | 11 | Riemann aims to make it easy to create application-specific transactions. It 12 | serializes and unserializes scripts from human-readable strings. It contains 13 | a complete toolbox for transaction construction, as well as built-in support 14 | for ~20 live networks and ~40 testnet or regtest nets. 15 | 16 | Riemann is NOT a wallet. It does NOT handle keys or create signatures. 17 | Riemann is NOT a protocol or RPC implementation. Riemann does NOT communicate 18 | with anything. Ever. Riemann is NOT a Script VM. Riemann does NOT check the 19 | validity of your scriptsigs. 20 | 21 | Riemann is _almost_ stateless. Before calling functions, you select from a list 22 | of :ref:`networks`. Tests are made using on-chain transactions, primarily from 23 | Bitcoin. 24 | 25 | .. toctree:: 26 | :maxdepth: 2 27 | :caption: Submodules: 28 | 29 | simple 30 | tx builder 31 | transactions 32 | encoding 33 | script 34 | networks 35 | utils 36 | 37 | Indices and tables 38 | ================== 39 | 40 | * :ref:`genindex` 41 | * :ref:`modindex` 42 | * :ref:`search` 43 | -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | Riemann: Cryptocurrency Transactions for Humans 2 | =============================================== 3 | 4 | .. code-block:: bash 5 | 6 | $ pip install riemann-tx 7 | 8 | Riemann is a **dependency-free Python3** library for creating **bitcoin-style 9 | transactions**. It is **compatible with many chains** and **supports SegWit**. 10 | 11 | Riemann aims to make it easy to create application-specific transactions. It 12 | serializes and unserializes scripts from human-readable strings. It contains 13 | a complete toolbox for transaction construction, as well as built-in support 14 | for ~20 live networks and ~40 testnet or regtest nets. 15 | 16 | Riemann is NOT a wallet. It does NOT handle keys or create signatures. 17 | Riemann is NOT a protocol or RPC implementation. Riemann does NOT communicate 18 | with anything. Ever. Riemann is NOT a Script VM. Riemann does NOT check the 19 | validity of your scriptsigs. 20 | 21 | Riemann is _almost_ stateless. Before calling functions, you select from a list 22 | of :ref:`networks`. Tests are made using on-chain transactions, primarily from 23 | Bitcoin. 24 | 25 | .. toctree:: 26 | :maxdepth: 2 27 | :caption: Submodules: 28 | 29 | simple 30 | tx builder 31 | transactions 32 | encoding 33 | script 34 | networks 35 | utils 36 | 37 | Indices and tables 38 | ================== 39 | 40 | * :ref:`genindex` 41 | * :ref:`modindex` 42 | * :ref:`search` 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Hi! Thanks for contributing to Riemann! 2 | 3 | We have a few rules for contributors. These rules are designed to protect 4 | contributors and users 5 | 6 | 1. Be cool 7 | 2. Sign your commits 8 | 3. Run the tests 9 | 4. Riemann is free software 10 | 11 | ### Be cool 12 | 13 | Riemann is a community project. Be a cool community. 14 | 15 | ### Sign your commits 16 | 17 | Riemann is critical infrastructure. We require signed commits. 18 | 19 | If you're not already set up to sign commits, follow these steps: 20 | 21 | 1. [Make a new GPG key](https://help.github.com/articles/generating-a-new-gpg-key/) 22 | 2. [Add the public key to your github account](https://help.github.com/articles/adding-a-new-gpg-key-to-your-github-account/) 23 | 3. [Set git to sign your commits](https://help.github.com/articles/telling-git-about-your-gpg-key/) 24 | 25 | If you've already made unsigned commits, that's no biggie. Set up signing and 26 | then squash your branch. We can help you out if necessary. 27 | 28 | ### Run the tests 29 | 30 | PRs will not be merged if they decrease test coverage. You can run the tests 31 | with `pytest`. For coverage details, run `tox`. 32 | 33 | ### Riemann is free software 34 | 35 | This code is all released under the LGPL. There's no CLA carve-out for the 36 | company. When you contribute to Riemann, your code will be released under the 37 | LGPL license. 38 | 39 | ### Contributing to docs 40 | 41 | Docstrings are mandatory going forward. You can find the RST source in 42 | `docs/source`. :) 43 | -------------------------------------------------------------------------------- /riemann/tests/script/SPQR.py: -------------------------------------------------------------------------------- 1 | # # Generates sighash values from available information on a raw transaction, 2 | # using python-bitcoinlib by Peter Todd and Merry Friends 3 | # https://github.com/petertodd/python-bitcoinlib/blob/master/LICENSE 4 | # Complies with BIP143. 5 | # Full title was "sighashgeneratorviapython-bitcoinlib.py", abbreviated to SPQR 6 | # pip install python-bitcoinlib 7 | 8 | import binascii 9 | from io import BytesIO 10 | from riemann.tests import helpers 11 | from bitcoin.core import CMutableTransaction 12 | from bitcoin.core.script import SIGHASH_ANYONECANPAY, CScript 13 | from bitcoin.core.script import SignatureHash, SIGHASH_ALL, SIGHASH_SINGLE 14 | 15 | 16 | def parse_tx(hex_tx): 17 | # NB: The deserialize function reads from a stream. 18 | raw_tx = BytesIO(binascii.unhexlify(hex_tx)) 19 | tx = CMutableTransaction.stream_deserialize(raw_tx) 20 | return tx 21 | 22 | 23 | prevout_pk_script = \ 24 | CScript(helpers.P2WPKH['ser']['ins'][0]['pk_script']) # bytes 25 | tx_hex = helpers.P2WPKH['ser']['tx']['unsigned'].hex() # raw hex 26 | index = helpers.P2WPKH['human']['ins'][0]['index'] # integer 27 | a = parse_tx(tx_hex) 28 | 29 | int_value = helpers.P2WPKH['human']['ins'][0]['value'] # integer of tx value 30 | 31 | print('Sighash All:') 32 | print(SignatureHash(prevout_pk_script, a, index, 33 | SIGHASH_ALL, sigversion=1, amount=(int_value)).hex()) 34 | print('Sighash All, Anyone Can Pay:') 35 | print(SignatureHash(prevout_pk_script, a, index, 36 | SIGHASH_ALL | SIGHASH_ANYONECANPAY, 37 | sigversion=1, amount=(int_value)).hex()) 38 | print('Sighash Single:') 39 | print(SignatureHash(prevout_pk_script, a, index, 40 | SIGHASH_SINGLE, sigversion=1, amount=(int_value)).hex()) 41 | print('Sighash Single, Anyone Can Pay:') 42 | print(SignatureHash(prevout_pk_script, a, index, 43 | SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, 44 | sigversion=1, amount=(int_value)).hex()) 45 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file only contains a selection of the most common options. For a full 6 | # list see the documentation: 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | sys.path.insert(0, os.path.abspath('../..')) 18 | sys.setrecursionlimit(1500) 19 | 20 | 21 | # -- Project information ----------------------------------------------------- 22 | 23 | project = 'riemann' 24 | copyright = '2019, James Prestwich' 25 | author = 'James Prestwich' 26 | 27 | # The full version, including alpha/beta/rc tags 28 | release = 'v2.0.3' 29 | 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 35 | # ones. 36 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.napoleon', 'sphinx.ext.autosectionlabel'] 37 | 38 | # Add any paths that contain templates here, relative to this directory. 39 | templates_path = ['_templates'] 40 | 41 | # List of patterns, relative to source directory, that match files and 42 | # directories to ignore when looking for source files. 43 | # This pattern also affects html_static_path and html_extra_path. 44 | exclude_patterns = [] 45 | 46 | 47 | # -- Options for HTML output ------------------------------------------------- 48 | 49 | # The theme to use for HTML and HTML Help pages. See the documentation for 50 | # a list of builtin themes. 51 | # 52 | html_theme = 'alabaster' 53 | 54 | # Add any paths that contain custom static files (such as style sheets) here, 55 | # relative to this directory. They are copied after the builtin static files, 56 | # so a file named "default.css" will overwrite the builtin "default.css". 57 | html_static_path = ['_static'] 58 | -------------------------------------------------------------------------------- /riemann/examples/sapling_example.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | from riemann import simple, utils 3 | from riemann.encoding import addresses as addr 4 | from riemann.script import serialization as ser 5 | 6 | riemann.select_network('zcash_sapling_main') 7 | 8 | prevout_addr_1 = 't1S3kN4zusjHtDEwfdaCMgk132bqo9DaYW4' 9 | prevout_addr_2 = 't1VQCUYzApF5eWf4UFAGdWwaFEpBfG2su1A' 10 | 11 | # This is the script code of the prevout being spent 12 | # We length prefix it 13 | # Needed for sighash later 14 | script_code_1 = b'\x19' + addr.to_output_script(prevout_addr_1) 15 | script_code_2 = b'\x19' + addr.to_output_script(prevout_addr_2) 16 | 17 | # Make inputs for our tx 18 | # We're spending the 1st output of a45216... 19 | # And the 0th output of ae9ee9... 20 | tx_in_1 = simple.unsigned_input(simple.outpoint('a45216a60855f053d63eb78a91429f85c6218541e876be95b17f8743635a0d3e', 1)) # noqa: E501 21 | tx_in_2 = simple.unsigned_input(simple.outpoint('ae9ee9ddeae0f0de07837f25b638ac8a723104753008d9c672e57b1d58e1c863', 0)) # noqa: E501 22 | 23 | # Make an output for our tx. 24 | # Our prevouts are worth 0.01845001 and 0.00002 ZEC 25 | # We want to pay 0.0001 ZEC 26 | tx_out = simple.output(1845001 + 2000 - 10000, 't1fRswMu1vopHpWVisgxTtkJSVs8ZCrDZtz') # noqa: E501 27 | 28 | # Make the transaction 29 | unsigned_tx = simple.unsigned_legacy_tx([tx_in_1, tx_in_2], [tx_out]) 30 | 31 | # Calculate the sighashes 32 | sighash_1 = unsigned_tx.sighash_all( 33 | index=0, 34 | script_code=script_code_1, 35 | prevout_value=utils.i2le_padded(1845001, 8)) 36 | sighash_2 = unsigned_tx.sighash_all( 37 | index=1, 38 | script_code=script_code_2, 39 | prevout_value=utils.i2le_padded(2000, 8)) 40 | 41 | # Sign the transaction with your private keys elsewhere 42 | pubkey_1 = '' 43 | pubkey_2 = '' 44 | 45 | sig_1 = '' 46 | sig_2 = '' 47 | 48 | # Build the stack scripts 49 | # For P2PKH this is the signature, then the pubkey 50 | stack_script_1 = '{} {}'.format(sig_1, pubkey_1) 51 | stack_script_2 = '{} {}'.format(sig_2, pubkey_2) 52 | 53 | # Add serialized stack scripts to the inputs 54 | signed_tx_in_1 = tx_in_1.copy(stack_script=ser.serialize(stack_script_1)) 55 | signed_tx_in_2 = tx_in_2.copy(stack_script=ser.serialize(stack_script_2)) 56 | 57 | # Add the signed inputs 58 | signed_tx = unsigned_tx.copy(tx_ins=[signed_tx_in_1, signed_tx_in_2]) 59 | 60 | # Print the hex-serialized transaction, ready for broadcast 61 | print(signed_tx.hex()) 62 | -------------------------------------------------------------------------------- /riemann/examples/sapling_htlc_example.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | from riemann import simple, utils 3 | from riemann.encoding import addresses as addr 4 | 5 | 6 | riemann.select_network('zcash_sapling_main') 7 | 8 | # Needs a 32 byte hash, alice's pubkey, a timeout, and bob's pubkey 9 | htlc_redeem_script = ( 10 | 'OP_IF ' 11 | 'OP_SHA256 {secret_hash} OP_EQUALVERIFY ' # noqa: E131 12 | 'OP_DUP OP_HASH160 {pkh0} ' 13 | 'OP_ELSE ' 14 | '{timeout} OP_CHECKLOCKTIMEVERIFY OP_DROP ' 15 | 'OP_DUP OP_HASH160 {pkh1} ' 16 | 'OP_ENDIF ' 17 | 'OP_EQUALVERIFY ' 18 | 'OP_CHECKSIG') 19 | 20 | # this spends via the HTLC secret revelation path 21 | # Needs (signature, pubkey, secret, TRUE) 22 | # IN THAT ORDER! 23 | htlc_stack_script_execute = '{sig} {pk} {secret} OP_1' 24 | 25 | # this spends via the timelocked refund path 26 | # Needs (signature, pubkey, FALSE serialized_redeem_script) 27 | # IN THAT ORDER! 28 | htlc_stack_script_refund = '{sig} {pk} OP_0' 29 | 30 | # Worst secret in the world :D 31 | secret = '32' * 32 32 | secret_hash = utils.sha256(bytes.fromhex(secret)).hex() 33 | 34 | # Use real pubkeys! 35 | fake_pk_execute = '02' * 32 36 | fake_pk_refund = '03' * 32 37 | 38 | # Use a real sig! 39 | fake_sig = '04' * 32 40 | 41 | # Use a real timelock! 42 | timeout = 'deadbeef' 43 | 44 | # string formatting to fill parameters 45 | filled_in_redeem_script = htlc_redeem_script.format( 46 | secret_hash=secret_hash, 47 | pkh0=utils.hash160(bytes.fromhex(fake_pk_execute)).hex(), 48 | timeout=timeout, 49 | pkh1=utils.hash160(bytes.fromhex(fake_pk_refund)).hex()) 50 | 51 | # DON'T SEND MONEY TO THIS EXAMPLE ADDRESS!!! 52 | # t 3fKPy737rsshnQJ7iRoXw3XujCB7tjuiUt 53 | htlc_address = addr.make_sh_address(filled_in_redeem_script) 54 | 55 | # how to send money there 56 | output = simple.output(500000, htlc_address) 57 | 58 | # --- HOW TO SPEND --- 59 | # fill in the sig/pubkey/secret 60 | filled_in_execute_script = htlc_stack_script_execute.format( 61 | sig=fake_sig, 62 | pk=fake_pk_execute, 63 | secret=secret) 64 | filled_in_refund_script = htlc_stack_script_refund.format( 65 | sig=fake_sig, 66 | pk=fake_pk_refund) 67 | 68 | # make inputs with the stack script and redeem script 69 | fake_outpoint = simple.empty_outpoint() 70 | execute_input = simple.p2sh_input( 71 | outpoint=fake_outpoint, 72 | stack_script=filled_in_execute_script, 73 | redeem_script=filled_in_redeem_script) 74 | refund_input = simple.p2sh_input( 75 | outpoint=fake_outpoint, 76 | stack_script=filled_in_refund_script, 77 | redeem_script=filled_in_redeem_script) 78 | -------------------------------------------------------------------------------- /riemann/tests/encoding/test_base58.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | import unittest 4 | import riemann 5 | from .. import helpers 6 | from ...encoding import base58 7 | 8 | # https://github.com/decred/dcrd/blob/dcc58786d33e557641948e1ac3f48785b2d11609/dcrutil/address_test.go 9 | DCR_ADDR = ( 10 | ('DsU7xcg53nxaKLLcAUSKyRndjG78Z2VZnX9', b'\x07\x3f', bytes([0x22, 0x9e, 0xba, 0xc3, 0x0e, 0xfd, 0x6a, 0x69, 0xee, 0xc9, 0xc1, 0xa4, 0x8e, 0x04, 0x8b, 0x7c, 0x97, 0x5c, 0x25, 0xf2])), 11 | ('DsUZxxoHJSty8DCfwfartwTYbuhmVct7tJu', b'\x07\x3f', bytes([0x27, 0x89, 0xd5, 0x8c, 0xfa, 0x09, 0x57, 0xd2, 0x06, 0xf0, 0x25, 0xc2, 0xaf, 0x05, 0x6f, 0xc8, 0xa7, 0x7c, 0xeb, 0xb0])), 12 | ('DcuQKx8BES9wU7C6Q5VmLBjw436r27hayjS', b'\x07\x1a', bytes([0xf0, 0xb4, 0xe8, 0x51, 0x00, 0xae, 0xe1, 0xa9, 0x96, 0xf2, 0x29, 0x15, 0xeb, 0x3c, 0x3f, 0x76, 0x4d, 0x53, 0x77, 0x9a])) 13 | ) 14 | 15 | 16 | class TestBase58(unittest.TestCase): 17 | 18 | def tearDown(self): 19 | riemann.select_network('bitcoin_main') 20 | 21 | def test_decode_error(self): 22 | with self.assertRaises(ValueError) as context: 23 | base58.decode('13VmALKHkCdSN1JULkP6RqW3LcbpWvgryW') 24 | 25 | self.assertIn( 26 | 'hashed base58 has bad checksum ', 27 | str(context.exception)) 28 | 29 | def test_decode_without_checksum(self): 30 | self.assertEqual( 31 | base58.decode('1P86rvoC4bTympTEdXnw9HhWVxb4', False), 32 | b'\x00' + helpers.PK['ser'][0]['pkh']) 33 | 34 | def test_encode_with_checksum(self): 35 | self.assertEqual( 36 | base58.encode_with_checksum(b'\x00' + helpers.PK['ser'][0]['pkh']), 37 | helpers.ADDR[0]['p2pkh']) 38 | 39 | def test_decode_with_checksum(self): 40 | self.assertEqual( 41 | b'\x00' + helpers.PK['ser'][0]['pkh'], 42 | base58.decode_with_checksum(helpers.ADDR[0]['p2pkh'])) 43 | 44 | def test_has_checksum(self): 45 | self.assertTrue( 46 | base58.has_checksum(helpers.ADDR[0]['p2pkh'])) 47 | self.assertFalse(base58.has_checksum('1P86rvoC4bTympTEdXnw9HhWVxb4')) 48 | 49 | def test_from_long_error(self): 50 | with self.assertRaises(ValueError) as context: 51 | base58.from_long(56, 1, 5, lambda: 1 / 0) # lambda always raises 52 | 53 | self.assertIn( 54 | "can't convert to character corresponding to", 55 | str(context.exception)) 56 | 57 | def test_decred_addresses(self): 58 | riemann.select_network('decred_main') 59 | for addr in DCR_ADDR: 60 | expected = addr[0] 61 | addr_hash = bytearray() 62 | addr_hash.extend(addr[1]) 63 | addr_hash.extend(addr[2]) 64 | self.assertEqual(expected, base58.encode(addr_hash)) 65 | -------------------------------------------------------------------------------- /riemann/examples/p2pkh_op_return.py: -------------------------------------------------------------------------------- 1 | from riemann import simple 2 | from riemann.tx import tx_builder 3 | from riemann.encoding import addresses as addr 4 | 5 | # For signing these example transactions, python-bitcoinlib is used: 6 | 7 | # from bitcoin.wallet import CKey 8 | # receiver = CKey(bytes.fromhex(receiver_privkey)) 9 | # receiver_pubkey = receiver.pub.hex() 10 | # sender = CKey(bytes.fromhex(sender_privkey)) 11 | # sender_pubkey = sender.pub.hex() 12 | 13 | 14 | version = 1 15 | sequence = 0xFFFFFFFE 16 | locktime = 0x00000000 17 | 18 | 19 | # Sender (made up private key that controls absolutely nothing) 20 | sender_privkey_hex = '372f913c52d7a6dfdfda9261e666a70e60f694f47c83fae388035fabbb168d63' # noqa: E501 21 | sender_pubkey = '02a004b949e4769ed341064829137b18992be884da5932c755e48f9465c1069dc2' # noqa: E501 22 | sender_addr = addr.make_p2wpkh_address(bytes.fromhex(sender_pubkey)) 23 | 24 | # Receiver (made up private key that controls absolutely nothing) 25 | receiver_privkey_hex = '9a597c337b95fb037e3e1e4b719b1fd8d3914e69c22464bee63954eda03b56c3' # noqa: E501 26 | receiver_pubkey = '02ef21caa25eca974d3bdd73c034d6943cbf145a700d493adaa6f496bd87c5b33b' # noqa: E501 27 | receiver_addr = addr.make_p2wpkh_address(bytes.fromhex(receiver_pubkey)) 28 | 29 | # Sender Input 30 | tx_id = 'ff7ff97060bfa1763dd9d4101b322157e841a4de865ddc28b1f71500f45c8135' 31 | index = 0 32 | sender_outpoint = simple.outpoint(tx_id, index) 33 | sender_value = 1000 34 | fee = 10 35 | sender_input = simple.unsigned_input(sender_outpoint, sequence=sequence) 36 | 37 | # Sender Output 38 | sender_output = simple.output(value=sender_value - fee, address=receiver_addr) 39 | 40 | # OP_RETURN output 41 | riemann_note = 'made with ❤ by riemann'.encode('utf-8') 42 | op_return_output = tx_builder.make_op_return_output(riemann_note) 43 | 44 | unsigned_tx = simple.unsigned_legacy_tx( 45 | tx_ins=[sender_input], 46 | tx_outs=[sender_output, op_return_output], 47 | version=version, 48 | lock_time=locktime) 49 | 50 | sighash_all = 0x01 51 | sighash = unsigned_tx.sighash_all( 52 | index=0, 53 | script=addr.to_output_script(sender_addr)) 54 | 55 | # Using instance of CKey from python-bitcoinlib to sign: 56 | # sig = sender.sign(sighash) + bytes([sighash_all]) 57 | # sig = sig.hex() 58 | sig = '30450221009e8c7f85d6491169df139f25d26633efe48e98738331a37a1694d655dccebdbd02201a6444cfb364e91279f8c9a8b09cdbdeb4bf6cc0f00f53b9356f852c3b3151dc01' # noqa: E501 59 | 60 | signed_input = simple.p2pkh_input( 61 | outpoint=sender_outpoint, 62 | sig=sig, 63 | pubkey=sender_pubkey, 64 | sequence=sequence) 65 | 66 | tx_signed = unsigned_tx.copy(tx_ins=[signed_input]) 67 | print('tx_signed') 68 | print(tx_signed.hex()) 69 | print() 70 | print(tx_signed.tx_id.hex()) 71 | print() 72 | print(tx_signed.tx_id_le.hex()) 73 | print() 74 | -------------------------------------------------------------------------------- /riemann/tests/test_simple.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import riemann 3 | from riemann import simple 4 | from riemann import tx as txn 5 | from riemann.tests import helpers 6 | 7 | 8 | class TestSimple(unittest.TestCase): 9 | 10 | def setUp(self): 11 | pass 12 | 13 | def tearDown(self): 14 | riemann.select_network('bitcoin_main') 15 | 16 | def test_output(self): 17 | for i in range(len(helpers.P2WSH['human']['outs'])): 18 | self.assertEqual( 19 | simple.output( 20 | value=helpers.P2WSH['human']['outs'][i]['value'], 21 | address=helpers.P2WSH['human']['outs'][i]['addr']), 22 | helpers.P2WSH['ser']['outs'][i]['output']) 23 | 24 | def test_outpoint(self): 25 | self.assertEqual( 26 | simple.outpoint( 27 | tx_id=helpers.P2PKH['human']['ins'][0]['hash'], 28 | index=helpers.P2PKH['human']['ins'][0]['index']), 29 | helpers.P2PKH['ser']['ins'][0]['outpoint']) 30 | 31 | def test_unsigned_input(self): 32 | outpoint = simple.outpoint( 33 | tx_id=helpers.P2PKH['human']['ins'][0]['hash'], 34 | index=helpers.P2PKH['human']['ins'][0]['index']) 35 | 36 | self.assertEqual( 37 | simple.unsigned_input( 38 | outpoint=outpoint), 39 | outpoint.to_bytes() + b'\x00' + b'\xFE\xFF\xFF\xFF') 40 | 41 | self.assertEqual( 42 | simple.unsigned_input( 43 | outpoint=outpoint, 44 | sequence=0x1234abcd), 45 | outpoint.to_bytes() + b'\x00' + b'\xcd\xab\x34\x12') 46 | 47 | def test_unsigned_legacy_tx(self): 48 | outpoint = simple.outpoint( 49 | tx_id=helpers.P2PKH['human']['ins'][0]['hash'], 50 | index=helpers.P2PKH['human']['ins'][0]['index']) 51 | tx_in = simple.unsigned_input( 52 | outpoint=outpoint, 53 | sequence=helpers.P2PKH['human']['ins'][0]['sequence']) 54 | tx_out = simple.output( 55 | helpers.P2PKH['human']['outs'][0]['value'], 56 | helpers.P2PKH['human']['outs'][0]['addr']) 57 | tx_return_output = txn.make_op_return_output( 58 | helpers.P2PKH['human']['outs'][1]['memo']) 59 | tx = simple.unsigned_legacy_tx( 60 | tx_ins=[tx_in], 61 | tx_outs=[tx_out, tx_return_output]) 62 | 63 | self.assertEqual(tx, helpers.P2PKH['ser']['tx']['unsigned']) 64 | 65 | def test_unsigned_witness_tx(self): 66 | outpoint = simple.outpoint( 67 | tx_id=helpers.P2WPKH['human']['ins'][0]['hash'], 68 | index=helpers.P2WPKH['human']['ins'][0]['index']) 69 | tx_in = simple.unsigned_input( 70 | outpoint=outpoint, 71 | sequence=helpers.P2WPKH['human']['ins'][0]['sequence']) 72 | tx_out = simple.output( 73 | helpers.P2WPKH['human']['outs'][0]['value'], 74 | helpers.P2WPKH['human']['outs'][0]['addr']) 75 | tx = simple.unsigned_witness_tx( 76 | tx_ins=[tx_in], 77 | tx_outs=[tx_out], 78 | lock_time=helpers.P2WPKH['human']['locktime']) 79 | 80 | self.assertEqual(tx, helpers.P2WPKH['ser']['tx']['unsigned']) 81 | -------------------------------------------------------------------------------- /riemann/networks/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # To add a new coin 4 | # 1. define a class in networks.py 5 | # 2. add it to SUPPORTED 6 | 7 | from .networks import * 8 | 9 | 10 | SUPPORTED = { 11 | 'bitcoin_main': BitcoinMain, 12 | 'bitcoin_test': BitcoinTest, 13 | 'bitcoin_reg': BitcoinRegtest, 14 | 'litecoin_main': LitecoinMain, 15 | 'litecoin_test': LitecoinTest, 16 | 'litecoin_reg': LitecoinRegtest, 17 | 'bitcoin_cash_main': BitcoinCashMain, 18 | 'bitcoin_cash_test': BitcoinCashTest, 19 | 'bitcoin_cash_reg': BitcoinCashRegtest, 20 | 'bitcoin_gold_main': BitcoinGoldMain, 21 | 'bitcoin_gold_test': BitcoinGoldTest, 22 | 'bitcoin_gold_reg': BitcoinGoldRegtest, 23 | 'dogecoin_main': DogecoinMain, 24 | 'dogecoin_test': DogecoinTest, 25 | 'dogecoin_reg': DogecoinRegtest, 26 | 'dash_main': DashMain, 27 | 'dash_test': DashTest, 28 | 'dash_reg': DashRegtest, 29 | 'zcash_sprout_main': ZcashSproutMain, 30 | 'zcash_sprout_test': ZcashSproutTest, 31 | 'zcash_sprout_reg': ZcashSproutRegtest, 32 | 'zcash_overwinter_main': ZcashOverwinterMain, 33 | 'zcash_overwinter_test': ZcashOverwinterTest, 34 | 'zcash_overwinter_reg': ZcashOverwinterRegtest, 35 | 'zcash_sapling_main': ZcashSaplingMain, 36 | 'zcash_sapling_test': ZcashSaplingTest, 37 | 'zcash_sapling_reg': ZcashSaplingRegtest, 38 | 'decred_main': DecredMain, 39 | 'decred_test': DecredTest, 40 | 'decred_simnet': DecredSimnet, 41 | 'pivx_main': PivxMain, 42 | 'pivx_test': PivxTest, 43 | 'pivx_reg': PivxRegtest, 44 | 'viacoin_main': ViacoinMain, 45 | 'viacoin_test': ViacoinTest, 46 | 'viacoin_simnet': ViacoinSimnet, 47 | 'feathercoin_main': FeathercoinMain, 48 | 'feathercoin_test': FeathercoinTest, 49 | 'feathercoin_reg': FeathercoinRegtest, 50 | 'bitcoin_dark_main': BitcoinDarkMain, 51 | 'bitcoin_dark_test': BitcoinDarkTest, 52 | 'bitcoin_dark_reg': BitcoinDarkRegtest, 53 | 'axe_main': AxeMain, 54 | 'axe_test': AxeTest, 55 | 'axe_reg': AxeRegtest, 56 | 'bitcore_main': BitcoreMain, 57 | 'bitcore_test': BitcoreTest, 58 | 'bitcore_reg': BitcoreRegtest, 59 | 'digibyte_main': DigibyteMain, 60 | 'digibyte_test': DigibyteTest, 61 | 'digibyte_reg': DigibyteRegtest, 62 | 'groestlcoin_main': GroestlcoinMain, 63 | 'groestlcoin_test': GroestlcoinTest, 64 | 'groestlcoin_reg': GroestlcoinRegtest, 65 | 'monacoin_main': MonacoinMain, 66 | 'monacoin_test': MonacoinTest, 67 | 'monacoin_reg': MonacoinRegtest, 68 | 'navcoin_main': NavcoinMain, 69 | 'navcoin_test': NavcoinTest, 70 | 'navcoin_reg': NavcoinRegtest, 71 | 'syscoin_main': SyscoinMain, 72 | 'syscoin_test': SyscoinTest, 73 | 'syscoin_reg': SyscoinRegtest, 74 | 'vertcoin_main': VertcoinMain, 75 | 'vertcoin_test': VertcoinTest, 76 | 'vertcoin_reg': VertcoinRegtest, 77 | 'bitcoin_private_main': BitcoinPrivateMain, 78 | 'bitcoin_private_test': BitcoinPrivateTest, 79 | 'bitcoin_private_reg': BitcoinPrivateRegtest, 80 | 'verge_main': VergeMain, 81 | 'verge_test': VergeTest, 82 | 'verge_reg': VergeRegtest 83 | } 84 | 85 | 86 | def get_network(name): 87 | ''' 88 | Check by name if network is supported. Then return the class. 89 | ''' 90 | if name not in SUPPORTED: 91 | raise ValueError('Unknown chain specifed: {}'.format(name)) 92 | 93 | return SUPPORTED[name] 94 | -------------------------------------------------------------------------------- /docs/txbuilder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tx Builder — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 |
29 | 30 | 31 |
32 | 33 |
34 |

Tx Builder

35 |
36 | 37 | 38 |
39 | 40 |
41 |
42 | 91 |
92 |
93 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Search — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |
34 | 35 | 36 |
37 | 38 |

Search

39 |
40 | 41 |

42 | Please activate JavaScript to enable the search 43 | functionality. 44 |

45 |
46 |

47 | From here you can search these documents. Enter your search 48 | words into the box below and click "search". Note that the search 49 | function will automatically search for all of the words. Pages 50 | containing fewer words won't appear in the result list. 51 |

52 |
53 | 54 | 55 | 56 |
57 | 58 |
59 | 60 |
61 | 62 |
63 | 64 |
65 |
66 | 106 |
107 |
108 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /riemann/tests/test_blake256.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | import unittest 4 | from ..blake256 import blake_hash 5 | 6 | TEST_VECTORS = [ 7 | ["716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", ""], 8 | ["43234ff894a9c0590d0246cfc574eb781a80958b01d7a2fa1ac73c673ba5e311", "a"], 9 | ["658c6d9019a1deddbcb3640a066dfd23471553a307ab941fd3e677ba887be329", "ab"], 10 | ["1833a9fa7cf4086bd5fda73da32e5a1d75b4c3f89d5c436369f9d78bb2da5c28", "abc"], 11 | ["35282468f3b93c5aaca6408582fced36e578f67671ed0741c332d68ac72d7aa2", "abcd"], 12 | ["9278d633efce801c6aa62987d7483d50e3c918caed7d46679551eed91fba8904", "abcde"], 13 | ["7a17ee5e289845adcafaf6ca1b05c4a281b232a71c7083f66c19ba1d1169a8d4", "abcdef"], 14 | ["ee8c7f94ff805cb2e644643010ea43b0222056420917ec70c3da764175193f8f", "abcdefg"], 15 | ["7b37c0876d29c5add7800a1823795a82b809fc12f799ff6a4b5e58d52c42b17e", "abcdefgh"], 16 | ["bdc514bea74ffbb9c3aa6470b08ceb80a88e313ad65e4a01457bbffd0acc86de", "abcdefghi"], 17 | ["12e3afb9739df8d727e93d853faeafc374cc55aedc937e5a1e66f5843b1d4c2e", "abcdefghij"], 18 | ["22297d373b751f581944bb26315133f6fda2f0bf60f65db773900f61f81b7e79", "Discard medicine more than two years old."], 19 | ["4d48d137bc9cf6d21415b805bf33f59320337d85c673998260e03a02a0d760cd", "He who has a shady past knows that nice guys finish last."], 20 | ["beba299e10f93e17d45663a6dc4b8c9349e4f5b9bac0d7832389c40a1b401e5c", "I wouldn't marry him with a ten foot pole."], 21 | ["42e082ae7f967781c6cd4e0ceeaeeb19fb2955adbdbaf8c7ec4613ac130071b3", "Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave"], 22 | ["207d06b205bfb359df91b48b6fd8aa6e4798b712d1cc5e91a254da9cef8684a3", "The days of the digital watch are numbered. -Tom Stoppard"], 23 | ["d56eab6927e371e2148b0788779aaf565d30567af2af822b6be3b90db9767a70", "Nepal premier won't resign."], 24 | ["01020709ca7fd10dc7756ce767d508d7206167d300b7a7ed76838a8547a7898c", "For every action there is an equal and opposite government program."], 25 | ["5569a6cc6535a66da221d8f6ad25008f28752d0343f3f1d757f1ecc9b1c61536", "His money is twice tainted: 'taint yours and 'taint mine."], 26 | ["8ff699b5ac7687c82600e89d0ff6cfa87e7179759184386971feb76fbae9975f", "There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977"], 27 | ["f4b3a7c85a418b15ce330fd41ae0254b036ad48dd98aa37f0506a995ba9c6029", "It's a tiny change to the code and not completely disgusting. - Bob Manchek"], 28 | ["1ed94bab64fe560ef0983165fcb067e9a8a971c1db8e6fb151ff9a7c7fe877e3", "size: a.out: bad magic"], 29 | ["ff15b54992eedf9889f7b4bbb16692881aa01ed10dfc860fdb04785d8185cd3c", "The major problem is with sendmail. -Mark Horton"], 30 | ["8a0a7c417a47deec0b6474d8c247da142d2e315113a2817af3de8f45690d8652", "Give me a rock, paper and scissors and I will move the world. CCFestoon"], 31 | ["310d263fdab056a930324cdea5f46f9ea70219c1a74b01009994484113222a62", "If the enemy is within range, then so are you."], 32 | ["1aaa0903aa4cf872fe494c322a6e535698ea2140e15f26fb6088287aedceb6ba", "It's well we cannot hear the screams/That we create in others' dreams."], 33 | ["2eb81bcaa9e9185a7587a1b26299dcfb30f2a58a7f29adb584b969725457ad4f", "You remind me of a TV show, but that's all right: I watch it anyway."], 34 | ["c27b1683ef76e274680ab5492e592997b0d9d5ac5a5f4651b6036f64215256af", "C is as portable as Stonehedge!!"], 35 | ["3995cce8f32b174c22ffac916124bd095c80205d9d5f1bb08a155ac24b40d6cb", "Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley"], 36 | ["496f7063f8bd479bf54e9d87e9ba53e277839ac7fdaecc5105f2879b58ee562f", "The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule"], 37 | ["2e0eff918940b01eea9539a02212f33ee84f77fab201f4287aa6167e4a1ed043", "How can you write a big system without C++? -Paul Glick"], 38 | ["7576698ee9cad30173080678e5965916adbb11cb5245d386bf1ffda1cb26c9d7", "The quick brown fox jumps over the lazy dog"], 39 | ["07663e00cf96fbc136cf7b1ee099c95346ba3920893d18cc8851f22ee2e36aa6", "BLAKE"], 40 | ["18a393b4e62b1887a2edf79a5c5a5464daf5bbb976f4007bea16a73e4c1e198e", "'BLAKE wins SHA-3! Hooray!!!' (I have time machine)"], 41 | ["1e75db2a709081f853c2229b65fd1558540aa5e7bd17b04b9a4b31989effa711", "HELP! I'm trapped in hash!"] 42 | ] 43 | 44 | class TestBlake(unittest.TestCase): 45 | 46 | def test_blake(self): 47 | for vectorSet in TEST_VECTORS: 48 | self.assertEqual(vectorSet[0], blake_hash(vectorSet[1]).hex()) 49 | -------------------------------------------------------------------------------- /riemann/simple.py: -------------------------------------------------------------------------------- 1 | from riemann import tx 2 | from riemann import utils 3 | from riemann.tx import decred 4 | from riemann.tx import tx_builder as tb 5 | from riemann.encoding import addresses as addr 6 | 7 | from typing import overload 8 | 9 | 10 | def output(value: int, address: str) -> tx.TxOut: 11 | ''' 12 | int, str -> TxOut 13 | accepts base58 or bech32 addresses 14 | ''' 15 | script = addr.to_output_script(address) 16 | value_bytes = utils.i2le_padded(value, 8) 17 | return tb._make_output(value_bytes, script) 18 | 19 | 20 | @overload 21 | def outpoint(tx_id: str, index: int, tree: int) -> decred.DecredOutpoint: 22 | ... 23 | 24 | 25 | @overload # noqa: F811 26 | def outpoint(tx_id: str, index: int) -> tx.Outpoint: 27 | ... 28 | 29 | 30 | def outpoint(tx_id, index, tree=None): # noqa: F811 31 | ''' 32 | Some overloads are not documented by Sphinx 33 | 34 | hex_str, int, int -> Outpoint 35 | accepts block explorer txid string 36 | ''' 37 | tx_id_le = bytes.fromhex(tx_id)[::-1] 38 | return tb.make_outpoint(tx_id_le, index, tree) 39 | 40 | 41 | @overload 42 | def unsigned_input( 43 | outpoint: tx.Outpoint, 44 | sequence: int) -> tx.TxIn: 45 | ... 46 | 47 | 48 | @overload # noqa: F811 49 | def unsigned_input( 50 | outpoint: decred.DecredOutpoint, 51 | sequence: int) -> decred.DecredTxIn: 52 | ... 53 | 54 | 55 | @overload # noqa: F811 56 | def unsigned_input( 57 | outpoint: decred.DecredOutpoint) -> decred.DecredTxIn: 58 | ... 59 | 60 | 61 | @overload # noqa: F811 62 | def unsigned_input( 63 | outpoint: tx.Outpoint) -> decred.DecredTxIn: 64 | ... 65 | 66 | 67 | def unsigned_input(outpoint, sequence=0xFFFFFFFE): # noqa: F811 68 | ''' 69 | Some overloads are not documented by Sphinx 70 | 71 | Outpoint, byte-like, int -> TxIn 72 | ''' 73 | return tb.make_legacy_input( 74 | outpoint=outpoint, 75 | stack_script=b'', 76 | redeem_script=b'', 77 | sequence=sequence) 78 | 79 | 80 | def unsigned_legacy_tx(tx_ins, tx_outs, **kwargs): 81 | '''Create an unsigned transaction 82 | Use this to generate sighashes for unsigned TxIns 83 | Hint: set version to 2 if using sequence number relative time locks 84 | 85 | Args: 86 | tx_ins list(TxIn instances): list of transaction inputs 87 | tx_outs list(TxOut instances): list of transaction outputs 88 | 89 | **kwargs: 90 | version (int): transaction version number 91 | lock_time (hex): transaction locktime 92 | expiry (int): overwinter expiry time 93 | tx_joinsplits (list): list of joinsplits transactions 94 | joinsplit_pubkey (bytes): joinsplit public key 95 | joinsplit_sig (bytes): joinsplit signature 96 | 97 | Returns: 98 | (Tx instance): unsigned transaction 99 | ''' 100 | return tb.make_tx( 101 | version=kwargs['version'] if 'version' in kwargs else 2, 102 | tx_ins=tx_ins, 103 | tx_outs=tx_outs, 104 | lock_time=kwargs['lock_time'] if 'lock_time' in kwargs else 0, 105 | expiry=kwargs['expiry'] if 'expiry' in kwargs else 0, 106 | tx_joinsplits=(kwargs['tx_joinsplits'] 107 | if 'tx_joinsplits' in kwargs else []), 108 | joinsplit_pubkey=(kwargs['joinsplit_pubkey'] 109 | if 'joinsplit_pubkey' in kwargs 110 | else []), 111 | joinsplit_sig=(kwargs['joinsplit_sig'] 112 | if 'joinsplit_sig' in kwargs else [])) 113 | 114 | 115 | def unsigned_witness_tx(tx_ins, tx_outs, **kwargs): 116 | '''Create an unsigned segwit transaction 117 | Create an unsigned segwit transaction 118 | Use this to generate sighashes for unsigned TxIns 119 | Hint: set version to 2 if using sequence number relative time locks 120 | 121 | Args: 122 | tx_ins list(TxIn instances): list of transaction inputs 123 | tx_outs list(TxOut instances): list of transaction outputs 124 | 125 | **kwargs: 126 | version (int): transaction version number 127 | locktime (hex): transaction locktime 128 | 129 | Returns: 130 | (Tx instance): unsigned transaction with empty witness 131 | ''' 132 | return tb.make_tx( 133 | version=kwargs['version'] if 'version' in kwargs else 2, 134 | tx_ins=tx_ins, 135 | tx_outs=tx_outs, 136 | lock_time=kwargs['lock_time'] if 'lock_time' in kwargs else 0, 137 | tx_witnesses=[tb.make_empty_witness() for _ in tx_ins]) 138 | -------------------------------------------------------------------------------- /riemann/utils.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import riemann 3 | from riemann import blake256 as b256 4 | 5 | 6 | def i2le(number: int) -> bytes: 7 | ''' 8 | Convert int to little endian (LE) bytes 9 | 10 | Args: 11 | number: int value to convert to bytes in LE format 12 | Returns: 13 | LE-encoded number 14 | ''' 15 | if number == 0: 16 | return b'\x00' 17 | return number.to_bytes((number.bit_length() + 7) // 8, 'little') 18 | 19 | 20 | def i2le_padded(number: int, length: int) -> bytes: 21 | ''' 22 | Convert int to little endian (LE) bytes with specified length 23 | 24 | Args: 25 | number: int value to convert to LE bytes 26 | length: length of resulting bytes 27 | Returns: 28 | LE-encoded number with fixed length 29 | ''' 30 | return number.to_bytes(length, 'little') 31 | 32 | 33 | def i2le_script(number: int) -> str: 34 | ''' 35 | Convert int to signed little endian (LE) hex for use within scripts 36 | 37 | Args: 38 | number: int value to convert to bytes in LE format 39 | Returns: 40 | the hex-encoded signed LE number 41 | ''' 42 | if number == 0: 43 | return '00' 44 | for i in range(520): 45 | try: # this is stupid 46 | return number.to_bytes( 47 | length=i, # minimal bytes lol 48 | byteorder='little', 49 | signed=True).hex() 50 | except OverflowError: 51 | continue 52 | raise ValueError( 53 | 'Number cannot be expressed in 520 bytes or less') # pragma: nocover 54 | 55 | 56 | def le2i(b: bytes, signed: bool = False) -> int: 57 | ''' 58 | Convert little endian (LE) bytes to int 59 | 60 | Args: 61 | b: LE bytes to convert to int 62 | signed: two's complement flag 63 | Returns: 64 | 65 | ''' 66 | return int.from_bytes(b, 'little', signed=signed) 67 | 68 | 69 | def be2i(b: bytes, signed: bool = False) -> int: 70 | ''' 71 | Convert big endian (b.e.) bytes to int 72 | 73 | Args: 74 | b: BE bytes to convert to int 75 | signed: two's complement flag 76 | Returns: 77 | 78 | ''' 79 | return int.from_bytes(b, 'big', signed=signed) 80 | 81 | 82 | def i2be(number: int) -> bytes: 83 | ''' 84 | Convert int to big endian (b.e.) bytes 85 | 86 | Args: 87 | number: int value to convert to bytes in BE format 88 | Returns: 89 | bytes in BE format 90 | ''' 91 | if number == 0: 92 | return b'\x00' 93 | return number.to_bytes((number.bit_length() + 7) // 8, 'big') 94 | 95 | 96 | def i2be_padded(number: int, length: int) -> bytes: 97 | ''' 98 | Convert int to big endian (b.e.) bytes with specified length 99 | 100 | Args: 101 | number: int value to convert to bytes in BE format 102 | length: length of resulting bytes 103 | Returns: 104 | bytes in BE format with specified length 105 | ''' 106 | return number.to_bytes(length, 'big') 107 | 108 | 109 | def change_endianness(b: bytes) -> bytes: 110 | '''Reverse a bytestring''' 111 | return b[::-1] 112 | 113 | 114 | def rmd160(msg_bytes: bytes) -> bytes: 115 | ''' 116 | ripemd160 digest of a messge 117 | ''' 118 | h = hashlib.new('ripemd160') 119 | h.update(msg_bytes) 120 | return h.digest() 121 | 122 | 123 | def sha256(msg_bytes: bytes) -> bytes: 124 | '''sha256 digest of a message''' 125 | return hashlib.sha256(msg_bytes).digest() 126 | 127 | 128 | def hash160(msg_bytes: bytes) -> bytes: 129 | '''rmd160 of sha256 of message''' 130 | h = hashlib.new('ripemd160') 131 | if 'decred' in riemann.get_current_network_name(): 132 | h.update(blake256(msg_bytes)) 133 | return h.digest() 134 | h.update(sha256(msg_bytes)) 135 | return h.digest() 136 | 137 | 138 | def hash256(msg_bytes: bytes) -> bytes: 139 | '''sha256 of sha256 of message''' 140 | if 'decred' in riemann.get_current_network_name(): 141 | return blake256(blake256(msg_bytes)) 142 | return hashlib.sha256(hashlib.sha256(msg_bytes).digest()).digest() 143 | 144 | 145 | def blake256(msg_bytes: bytes) -> bytes: 146 | '''blake256 digest of a message''' 147 | return b256.blake_hash(msg_bytes) 148 | 149 | 150 | def blake2b(data: bytes = b'', **kwargs) -> bytes: 151 | '''blake2b digest of a message''' 152 | b2 = hashlib.blake2b(**kwargs) 153 | b2.update(data) 154 | return b2.digest() 155 | 156 | 157 | def blake2s(data: bytes = b'', **kwargs) -> bytes: 158 | '''blake2s digest of a message''' 159 | b2 = hashlib.blake2s(**kwargs) 160 | b2.update(data) 161 | return b2.digest() 162 | -------------------------------------------------------------------------------- /riemann/encoding/cashaddr.py: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2017 Shammah Chancellor 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 THE 21 | # SOFTWARE. 22 | 23 | 24 | import riemann 25 | 26 | CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l' 27 | 28 | 29 | def encode(data: bytes) -> str: 30 | '''Convert bytes to cashaddr-bech32''' 31 | if riemann.network.CASHADDR_PREFIX is None: 32 | raise ValueError('Network {} does not support cashaddresses.' 33 | .format(riemann.get_current_network_name())) 34 | 35 | data = convertbits(data, 8, 5) 36 | checksum = calculate_checksum(riemann.network.CASHADDR_PREFIX, data) 37 | 38 | payload = b32encode(data + checksum) 39 | 40 | form = '{prefix}:{payload}' 41 | return form.format( 42 | prefix=riemann.network.CASHADDR_PREFIX, 43 | payload=payload) 44 | 45 | 46 | def decode(data: str) -> bytes: 47 | '''Convert cashaddr-bech32 to bytes''' 48 | 49 | if riemann.network.CASHADDR_PREFIX is None: 50 | raise ValueError('Network {} does not support cashaddresses.' 51 | .format(riemann.get_current_network_name())) 52 | if data.find(riemann.network.CASHADDR_PREFIX) != 0: 53 | raise ValueError('Malformed cashaddr. Cannot locate prefix: {}' 54 | .format(riemann.network.CASHADDR_PREFIX)) 55 | 56 | # the data is everything after the colon 57 | prefix, data = data.split(':') 58 | decoded = b32decode(data) 59 | if not verify_checksum(prefix, decoded): 60 | raise ValueError('Bad cash address checksum') 61 | converted = convertbits(decoded, 5, 8) 62 | 63 | return bytes(converted[:-6]) # remove the checksum from the end 64 | 65 | 66 | def polymod(values): 67 | chk = 1 68 | generator = [ 69 | (0x01, 0x98f2bc8e61), 70 | (0x02, 0x79b76d99e2), 71 | (0x04, 0xf33e5fb3c4), 72 | (0x08, 0xae2eabe2a8), 73 | (0x10, 0x1e4f43e470)] 74 | for value in values: 75 | top = chk >> 35 76 | chk = ((chk & 0x07ffffffff) << 5) ^ value 77 | for i in generator: 78 | if top & i[0] != 0: 79 | chk ^= i[1] 80 | return chk ^ 1 81 | 82 | 83 | def prefix_expand(prefix): 84 | return [ord(x) & 0x1f for x in prefix] + [0] 85 | 86 | 87 | def calculate_checksum(prefix, payload): 88 | poly = polymod(prefix_expand(prefix) + payload + [0, 0, 0, 0, 0, 0, 0, 0]) 89 | out = list() 90 | for i in range(8): 91 | out.append((poly >> 5 * (7 - i)) & 0x1f) 92 | return out 93 | 94 | 95 | def verify_checksum(prefix, payload): 96 | return polymod(prefix_expand(prefix) + payload) == 0 97 | 98 | 99 | def b32decode(inputs): 100 | out = list() 101 | for letter in inputs: 102 | out.append(CHARSET.find(letter)) 103 | return out 104 | 105 | 106 | def b32encode(inputs): 107 | out = '' 108 | for char_code in inputs: 109 | out += CHARSET[char_code] 110 | return out 111 | 112 | 113 | def convertbits(data, frombits, tobits, pad=True): 114 | acc = 0 115 | bits = 0 116 | ret = [] 117 | maxv = (1 << tobits) - 1 118 | max_acc = (1 << (frombits + tobits - 1)) - 1 119 | for value in data: 120 | if value < 0 or (value >> frombits): 121 | return None 122 | acc = ((acc << frombits) | value) & max_acc 123 | bits += frombits 124 | while bits >= tobits: 125 | bits -= tobits 126 | ret.append((acc >> bits) & maxv) 127 | if pad: 128 | if bits: 129 | ret.append((acc << (tobits - bits)) & maxv) 130 | elif bits >= frombits or ((acc << (tobits - bits)) & maxv): 131 | return None 132 | return ret 133 | -------------------------------------------------------------------------------- /riemann/script/opcodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Enumerate the opcodes of the Bitcoin VM. 4 | 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2013 by Richard Kiss 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | """ 28 | 29 | from typing import Dict, List, Tuple 30 | 31 | OPCODE_LIST: List[Tuple[str, int]] = [ 32 | ("OP_0", 0), 33 | ("OP_PUSHDATA1", 76), 34 | ("OP_PUSHDATA2", 77), 35 | ("OP_PUSHDATA4", 78), 36 | ("OP_1NEGATE", 79), 37 | ("OP_RESERVED", 80), 38 | ("OP_1", 81), 39 | ("OP_2", 82), 40 | ("OP_3", 83), 41 | ("OP_4", 84), 42 | ("OP_5", 85), 43 | ("OP_6", 86), 44 | ("OP_7", 87), 45 | ("OP_8", 88), 46 | ("OP_9", 89), 47 | ("OP_10", 90), 48 | ("OP_11", 91), 49 | ("OP_12", 92), 50 | ("OP_13", 93), 51 | ("OP_14", 94), 52 | ("OP_15", 95), 53 | ("OP_16", 96), 54 | ("OP_NOP", 97), 55 | ("OP_VER", 98), 56 | ("OP_IF", 99), 57 | ("OP_NOTIF", 100), 58 | ("OP_VERIF", 101), 59 | ("OP_VERNOTIF", 102), 60 | ("OP_ELSE", 103), 61 | ("OP_ENDIF", 104), 62 | ("OP_VERIFY", 105), 63 | ("OP_RETURN", 106), 64 | ("OP_TOALTSTACK", 107), 65 | ("OP_FROMALTSTACK", 108), 66 | ("OP_2DROP", 109), 67 | ("OP_2DUP", 110), 68 | ("OP_3DUP", 111), 69 | ("OP_2OVER", 112), 70 | ("OP_2ROT", 113), 71 | ("OP_2SWAP", 114), 72 | ("OP_IFDUP", 115), 73 | ("OP_DEPTH", 116), 74 | ("OP_DROP", 117), 75 | ("OP_DUP", 118), 76 | ("OP_NIP", 119), 77 | ("OP_OVER", 120), 78 | ("OP_PICK", 121), 79 | ("OP_ROLL", 122), 80 | ("OP_ROT", 123), 81 | ("OP_SWAP", 124), 82 | ("OP_TUCK", 125), 83 | ("OP_CAT", 126), 84 | ("OP_SUBSTR", 127), 85 | ("OP_LEFT", 128), 86 | ("OP_RIGHT", 129), 87 | ("OP_SIZE", 130), 88 | ("OP_INVERT", 131), 89 | ("OP_AND", 132), 90 | ("OP_OR", 133), 91 | ("OP_XOR", 134), 92 | ("OP_EQUAL", 135), 93 | ("OP_EQUALVERIFY", 136), 94 | ("OP_RESERVED1", 137), 95 | ("OP_RESERVED2", 138), 96 | ("OP_1ADD", 139), 97 | ("OP_1SUB", 140), 98 | ("OP_2MUL", 141), 99 | ("OP_2DIV", 142), 100 | ("OP_NEGATE", 143), 101 | ("OP_ABS", 144), 102 | ("OP_NOT", 145), 103 | ("OP_0NOTEQUAL", 146), 104 | ("OP_ADD", 147), 105 | ("OP_SUB", 148), 106 | ("OP_MUL", 149), 107 | ("OP_DIV", 150), 108 | ("OP_MOD", 151), 109 | ("OP_LSHIFT", 152), 110 | ("OP_RSHIFT", 153), 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 | ("OP_RIPEMD160", 166), 124 | ("OP_SHA1", 167), 125 | ("OP_SHA256", 168), 126 | ("OP_HASH160", 169), 127 | ("OP_HASH256", 170), 128 | ("OP_CODESEPARATOR", 171), 129 | ("OP_CHECKSIG", 172), 130 | ("OP_CHECKSIGVERIFY", 173), 131 | ("OP_CHECKMULTISIG", 174), 132 | ("OP_CHECKMULTISIGVERIFY", 175), 133 | ("OP_NOP1", 176), 134 | ("OP_NOP2", 177), 135 | ("OP_CHECKLOCKTIMEVERIFY", 177), 136 | ("OP_NOP3", 178), 137 | ("OP_CHECKSEQUENCEVERIFY", 178), 138 | ("OP_NOP4", 179), 139 | ("OP_NOP5", 180), 140 | ("OP_NOP6", 181), 141 | ("OP_NOP7", 182), 142 | ("OP_NOP8", 183), 143 | ("OP_NOP9", 184), 144 | ("OP_NOP10", 185), 145 | ("OP_INVALIDOPCODE", 255), 146 | ] 147 | 148 | for i in range(1, 76): 149 | OPCODE_LIST.append(("OP_PUSH_%d" % i, i)) 150 | 151 | CODE_TO_INT: Dict[str, int] = dict(o for o in OPCODE_LIST) 152 | INT_TO_CODE: Dict[int, str] = dict((o[1], o[0]) for o in OPCODE_LIST) 153 | -------------------------------------------------------------------------------- /riemann/tests/tx/test_overwinter.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | import unittest 3 | from riemann import tx 4 | from riemann.tests.tx.helpers import overwinter_helpers as helpers 5 | 6 | 7 | class OverwinterTestCase(unittest.TestCase): 8 | 9 | def __init__(self, *args, **kwargs): 10 | super().__init__(*args, **kwargs) 11 | 12 | def setUp(self): 13 | riemann.select_network('zcash_overwinter_main') 14 | 15 | def tearDown(self): 16 | riemann.select_network('bitcoin_main') 17 | 18 | 19 | class TestOverwinterTx(OverwinterTestCase): 20 | 21 | def setUp(self): 22 | super().setUp() 23 | self.joinsplit_ser = helpers.ZCASH_SPROUT['ser']['joinsplit_0'] 24 | self.zkproof = tx.SproutZkproof.from_bytes( 25 | helpers.ZCASH_SPROUT['ser']['joinsplits'][0]['proof']) 26 | self.joinsplit = helpers.ZCASH_SPROUT['ser']['joinsplits'][0].copy() 27 | self.joinsplit['zkproof'] = self.zkproof 28 | self.joinsplit.pop('proof') # remove this entry from the dict 29 | self.joinsplit = tx.SproutJoinsplit(**self.joinsplit) 30 | 31 | self.tx_ser = helpers.ZCASH_SPROUT['ser']['tx'] 32 | 33 | self.tx = {} 34 | 35 | self.tx['tx_ins'] = [] 36 | self.tx_out = tx.TxOut.from_bytes( 37 | helpers.ZCASH_SPROUT['ser']['tx_out_0']) 38 | self.tx['tx_outs'] = [self.tx_out] 39 | self.tx['lock_time'] = helpers.ZCASH_SPROUT['ser']['lock_time'] 40 | self.tx['expiry_height'] = helpers.ZCASH_SPROUT['ser']['lock_time'] 41 | self.tx['tx_joinsplits'] = [self.joinsplit] 42 | self.tx['joinsplit_pubkey'] = \ 43 | helpers.ZCASH_SPROUT['ser']['joinsplit_pubkey'] 44 | self.tx['joinsplit_sig'] = helpers.ZCASH_SPROUT['ser']['joinsplit_sig'] 45 | 46 | def attr_assert(self, attr_name, replacement, err_text): 47 | # Removes a named key from a dictionary and replaces it with b'\x00' 48 | temp_dict = dict((a, self.tx[a]) 49 | for a in self.tx 50 | if a != attr_name) 51 | temp_dict[attr_name] = replacement 52 | with self.assertRaises(ValueError) as context: 53 | tx.OverwinterTx(**temp_dict) 54 | 55 | self.assertIn(err_text, str(context.exception)) 56 | 57 | def test_init_errors(self): 58 | self.attr_assert('lock_time', b'', 'Expected byte-like object') 59 | self.attr_assert('expiry_height', b'', 'Expected byte-like object') 60 | self.attr_assert('expiry_height', b'\xff' * 4, 'Expiry time too high') 61 | self.attr_assert('tx_ins', [b''], 'Invalid TxIn. ') 62 | self.attr_assert('tx_outs', [b''], 'Invalid TxOut. ') 63 | self.attr_assert('tx_joinsplits', [b''] * 6, 'Too many joinsplits.') 64 | self.attr_assert('tx_joinsplits', [b''], 'Invalid Joinsplit. ') 65 | self.attr_assert('tx_joinsplits', [], 'Transaction must have ') 66 | self.attr_assert('joinsplit_pubkey', b'', 'Expected byte-like object') 67 | self.attr_assert('joinsplit_sig', b'\x00', 'Expected byte-like object') 68 | 69 | def test_calculate_fee(self): 70 | t = tx.OverwinterTx(**self.tx) 71 | self.assertEqual( 72 | t.calculate_fee([]), 73 | 10000) 74 | 75 | def test_not_overwinter(self): 76 | riemann.select_network('zcash_sprout_main') 77 | with self.assertRaises(ValueError) as context: 78 | tx.OverwinterTx(None, None, None, None, 79 | None, None, None) 80 | self.assertIn( 81 | 'OverwinterTx not supported by network ', 82 | str(context.exception)) 83 | 84 | def test_from_bytes_no_js(self): 85 | self.assertIsInstance( 86 | tx.OverwinterTx.from_bytes( 87 | helpers.ZCASH_OVERWINTER_NO_JS['ser']['tx']), 88 | tx.OverwinterTx) 89 | 90 | 91 | class OverwinterSighash(unittest.TestCase): 92 | 93 | def setUp(self): 94 | riemann.select_network('zcash_overwinter_main') 95 | self.tx = tx.OverwinterTx.from_bytes(helpers.RAW_TX) 96 | 97 | def test_hash_prevouts(self): 98 | self.assertEqual( 99 | self.tx._hash_prevouts(anyone_can_pay=False), 100 | helpers.HASH_PREVOUTS) 101 | 102 | def test_hash_outputs(self): 103 | self.assertEqual( 104 | self.tx._hash_outputs(tx.SIGHASH_SINGLE, index=1), 105 | helpers.HASH_OUTPUTS) 106 | 107 | def test_hash_joinsplits(self): 108 | self.assertEqual( 109 | self.tx._hash_joinsplits(), 110 | helpers.HASH_JOINSPLITS) 111 | 112 | def test_sighash(self): 113 | self.assertEqual( 114 | self.tx.sighash_single( 115 | tx.SIGHASH_SINGLE, 116 | index=1, 117 | script_code=helpers.SCRIPT_CODE, 118 | prevout_value=helpers.PREVOUT_VALUE), 119 | helpers.SIGHASH) 120 | -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f8f8f8; } 3 | .highlight .c { color: #8f5902; font-style: italic } /* Comment */ 4 | .highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ 5 | .highlight .g { color: #000000 } /* Generic */ 6 | .highlight .k { color: #004461; font-weight: bold } /* Keyword */ 7 | .highlight .l { color: #000000 } /* Literal */ 8 | .highlight .n { color: #000000 } /* Name */ 9 | .highlight .o { color: #582800 } /* Operator */ 10 | .highlight .x { color: #000000 } /* Other */ 11 | .highlight .p { color: #000000; font-weight: bold } /* Punctuation */ 12 | .highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ 13 | .highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ 14 | .highlight .cp { color: #8f5902 } /* Comment.Preproc */ 15 | .highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ 16 | .highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ 17 | .highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ 18 | .highlight .gd { color: #a40000 } /* Generic.Deleted */ 19 | .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ 20 | .highlight .gr { color: #ef2929 } /* Generic.Error */ 21 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 22 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 23 | .highlight .go { color: #888888 } /* Generic.Output */ 24 | .highlight .gp { color: #745334 } /* Generic.Prompt */ 25 | .highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ 26 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 27 | .highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ 28 | .highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ 29 | .highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ 30 | .highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ 31 | .highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ 32 | .highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ 33 | .highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ 34 | .highlight .ld { color: #000000 } /* Literal.Date */ 35 | .highlight .m { color: #990000 } /* Literal.Number */ 36 | .highlight .s { color: #4e9a06 } /* Literal.String */ 37 | .highlight .na { color: #c4a000 } /* Name.Attribute */ 38 | .highlight .nb { color: #004461 } /* Name.Builtin */ 39 | .highlight .nc { color: #000000 } /* Name.Class */ 40 | .highlight .no { color: #000000 } /* Name.Constant */ 41 | .highlight .nd { color: #888888 } /* Name.Decorator */ 42 | .highlight .ni { color: #ce5c00 } /* Name.Entity */ 43 | .highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ 44 | .highlight .nf { color: #000000 } /* Name.Function */ 45 | .highlight .nl { color: #f57900 } /* Name.Label */ 46 | .highlight .nn { color: #000000 } /* Name.Namespace */ 47 | .highlight .nx { color: #000000 } /* Name.Other */ 48 | .highlight .py { color: #000000 } /* Name.Property */ 49 | .highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ 50 | .highlight .nv { color: #000000 } /* Name.Variable */ 51 | .highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ 52 | .highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ 53 | .highlight .mb { color: #990000 } /* Literal.Number.Bin */ 54 | .highlight .mf { color: #990000 } /* Literal.Number.Float */ 55 | .highlight .mh { color: #990000 } /* Literal.Number.Hex */ 56 | .highlight .mi { color: #990000 } /* Literal.Number.Integer */ 57 | .highlight .mo { color: #990000 } /* Literal.Number.Oct */ 58 | .highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ 59 | .highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ 60 | .highlight .sc { color: #4e9a06 } /* Literal.String.Char */ 61 | .highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ 62 | .highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ 63 | .highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ 64 | .highlight .se { color: #4e9a06 } /* Literal.String.Escape */ 65 | .highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ 66 | .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ 67 | .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ 68 | .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ 69 | .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ 70 | .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ 71 | .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ 72 | .highlight .fm { color: #000000 } /* Name.Function.Magic */ 73 | .highlight .vc { color: #000000 } /* Name.Variable.Class */ 74 | .highlight .vg { color: #000000 } /* Name.Variable.Global */ 75 | .highlight .vi { color: #000000 } /* Name.Variable.Instance */ 76 | .highlight .vm { color: #000000 } /* Name.Variable.Magic */ 77 | .highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /riemann/encoding/base58.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2013 by Richard Kiss 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. 22 | 23 | '''Implementation of Base58 encoding with checksum''' 24 | 25 | from riemann import utils 26 | from typing import Callable, Tuple 27 | 28 | BASE58_ALPHABET = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 29 | BASE58_BASE = len(BASE58_ALPHABET) 30 | BASE58_LOOKUP = dict((c, i) for i, c in enumerate(BASE58_ALPHABET)) 31 | 32 | 33 | def encode(data: bytes, checksum: bool = True) -> str: 34 | '''Convert binary to base58 using BASE58_ALPHABET.''' 35 | 36 | if checksum: 37 | data = data + utils.hash256(data)[:4] 38 | v, prefix = to_long(256, lambda x: x, data) 39 | data = from_long(v, prefix, BASE58_BASE, lambda v: BASE58_ALPHABET[v]) 40 | return data.decode("utf8") 41 | 42 | 43 | def decode(s: str, checksum: bool = True) -> bytes: 44 | '''Convert base58 to binary using BASE58_ALPHABET.''' 45 | v, prefix = to_long( 46 | BASE58_BASE, lambda c: BASE58_LOOKUP[c], s.encode("utf8")) 47 | 48 | data = from_long(v, prefix, 256, lambda x: x) 49 | 50 | if checksum: 51 | data, the_hash = data[:-4], data[-4:] 52 | if utils.hash256(data)[:4] == the_hash: 53 | return data 54 | raise ValueError("hashed base58 has bad checksum %s" % s) 55 | 56 | return data 57 | 58 | 59 | def encode_with_checksum(data: bytes) -> str: 60 | ''' 61 | A "hashed_base58" structure is a base58 integer (which looks like a string) 62 | with four bytes of hash data at the end. 63 | 64 | This function turns data into its hashed_base58 equivalent. 65 | ''' 66 | return encode(data, checksum=True) 67 | 68 | 69 | def decode_with_checksum(s: str) -> bytes: 70 | ''' 71 | If the passed string is base58check, return the binary data. 72 | 73 | Otherwise raises a ValueError. 74 | ''' 75 | return decode(s, checksum=True) 76 | 77 | 78 | def has_checksum(base58: str) -> bool: 79 | '''Return True if and only if base58 is valid hashed_base58.''' 80 | try: 81 | decode_with_checksum(base58) 82 | except ValueError: 83 | return False 84 | return True 85 | 86 | 87 | def from_long( 88 | v: int, 89 | prefix: int, 90 | base: int, 91 | charset: Callable[..., int]) -> bytes: 92 | ''' 93 | The inverse of to_long. Convert an integer to an arbitrary base. 94 | 95 | Args: 96 | v: the integer value to convert 97 | prefix: the number of prefixed 0s to include 98 | base: the new radix 99 | charset: an array indicating printable characters to use for each value 100 | ''' 101 | ba = bytearray() 102 | while v > 0: 103 | try: 104 | v, mod = divmod(v, base) 105 | ba.append(charset(mod)) 106 | except Exception: 107 | raise ValueError( 108 | "can't convert to character corresponding to %d" % mod) 109 | ba.extend([charset(0)] * prefix) 110 | ba.reverse() 111 | return bytes(ba) 112 | 113 | 114 | def to_long( 115 | base: int, 116 | lookup_f: Callable[..., int], 117 | s: bytes) -> Tuple[int, int]: 118 | ''' 119 | Convert an array to a (possibly bignum) integer, along with a prefix value 120 | of how many prefixed zeros there are. 121 | 122 | Args: 123 | base: the source radix 124 | lookup_f: a function to convert an element of s to a value between 0 125 | and base-1. 126 | s: the value to convert 127 | ''' 128 | prefix = 0 129 | v = 0 130 | for c in s: 131 | v *= base 132 | try: 133 | v += lookup_f(c) 134 | except Exception: 135 | raise ValueError(f"bad character {c!r} in string {s!r}") 136 | if v == 0: 137 | prefix += 1 138 | return v, prefix 139 | -------------------------------------------------------------------------------- /riemann/tests/script/test_serialization.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import riemann 3 | from riemann.tests import helpers 4 | from riemann.script import serialization as ser 5 | 6 | 7 | class TestSerialization(unittest.TestCase): 8 | 9 | def tearDown(self): 10 | riemann.select_network('bitcoin_main') 11 | 12 | def test_serialize(self): 13 | self.assertEqual( 14 | ser.serialize(helpers.MSIG_2_2['redeem_script']), 15 | helpers.MSIG_2_2['ser_script']) 16 | self.assertEqual( 17 | ser.serialize('OP_IF'), 18 | bytes([99])) 19 | 20 | def test_serialize_error(self): 21 | with self.assertRaises(NotImplementedError) as context: 22 | ser.serialize('00' * 65999) 23 | self.assertIn( 24 | 'Hex string too long to serialize.', 25 | str(context.exception)) 26 | 27 | with self.assertRaises(NotImplementedError) as context: 28 | ser.serialize('OP_PUSHDATA4') 29 | self.assertIn( 30 | 'OP_PUSHDATA4 is a bad idea.', 31 | str(context.exception)) 32 | 33 | with self.assertRaises(NotImplementedError) as context: 34 | ser.serialize('OP_CODESEPARATOR') 35 | self.assertIn( 36 | 'OP_CODESEPARATOR is a bad idea.', 37 | str(context.exception)) 38 | 39 | with self.assertRaises(ValueError) as context: 40 | ser.serialize('OP_NOTAREALOPCODE') 41 | self.assertIn( 42 | 'non-hexadecimal number found', 43 | str(context.exception)) 44 | 45 | def test_hex_serialize(self): 46 | self.assertEqual( 47 | ser.hex_serialize(helpers.MSIG_2_2['redeem_script']), 48 | helpers.MSIG_2_2['ser_script'].hex()) 49 | 50 | self.assertEqual( 51 | ser.hex_serialize('OP_IF'), 52 | bytes([99]).hex()) 53 | 54 | def test_hex_serialize_OP_PUSHDATA1(self): 55 | self.assertEqual( 56 | ser.hex_serialize( 57 | helpers.P2SH_PD1['human']['ins'][0]['script_sig']), 58 | helpers.P2SH_PD1['ser']['ins'][0]['script_sig'].hex()) 59 | 60 | def test_hex_deserialize_OP_PUSHDATA1(self): 61 | self.assertEqual( 62 | ser.hex_deserialize( 63 | helpers.P2SH_PD1['ser']['ins'][0]['script_sig'].hex()), 64 | helpers.P2SH_PD1['human']['ins'][0]['script_sig']) 65 | 66 | def test_hex_serialize_OP_PUSHDATA2(self): 67 | self.assertEqual( 68 | ser.hex_serialize( 69 | helpers.P2SH_PD2['human']['ins'][0]['script_sig']), 70 | helpers.P2SH_PD2['ser']['ins'][0]['script_sig'].hex()) 71 | 72 | def test_hex_deserialize_OP_PUSHDATA2(self): 73 | self.assertEqual( 74 | ser.hex_deserialize( 75 | helpers.P2SH_PD2['ser']['ins'][0]['script_sig'].hex()), 76 | helpers.P2SH_PD2['human']['ins'][0]['script_sig']) 77 | 78 | def test_deserialize(self): 79 | self.assertEqual( 80 | helpers.MSIG_2_2['redeem_script'], 81 | ser.deserialize(helpers.MSIG_2_2['ser_script'])) 82 | 83 | self.assertEqual( 84 | 'OP_IF', 85 | ser.deserialize(bytes([99]))) 86 | 87 | def test_deserialize_error(self): 88 | with self.assertRaises(IndexError) as context: 89 | ser.deserialize(b'\x05\x00\x00') 90 | self.assertIn( 91 | 'Push 5 caused out of bounds exception.', 92 | str(context.exception)) 93 | 94 | with self.assertRaises(NotImplementedError) as context: 95 | ser.deserialize(b'\xab') 96 | self.assertIn( 97 | 'OP_CODESEPARATOR is a bad idea.', 98 | str(context.exception)) 99 | 100 | with self.assertRaises(ValueError) as context: 101 | ser.deserialize(b'\xfe') 102 | self.assertIn( 103 | 'Unsupported opcode. Got 0xfe', 104 | str(context.exception)) 105 | 106 | def test_hex_deserialize(self): 107 | self.assertEqual( 108 | helpers.MSIG_2_2['redeem_script'], 109 | ser.hex_deserialize(helpers.MSIG_2_2['ser_script'].hex())) 110 | 111 | self.assertEqual( 112 | 'OP_IF', 113 | ser.hex_deserialize('63')) 114 | 115 | def test_overwrites(self): 116 | riemann.select_network('decred_main') 117 | self.assertEqual( 118 | 'OP_BLAKE256', 119 | ser.hex_deserialize('a8')) 120 | self.assertEqual( 121 | 'a8', 122 | ser.hex_serialize('OP_BLAKE256')) 123 | self.assertEqual( 124 | 'OP_SHA256', 125 | ser.hex_deserialize('c0')) 126 | self.assertEqual( 127 | 'c0', 128 | ser.hex_serialize('OP_SHA256')) 129 | 130 | def test_pushdata4_error(self): 131 | with self.assertRaises(NotImplementedError) as context: 132 | ser.deserialize(bytes([78])) 133 | 134 | self.assertIn( 135 | 'OP_PUSHDATA4 is a bad idea.', 136 | str(context.exception)) 137 | -------------------------------------------------------------------------------- /riemann/script/serialization.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | from riemann import utils 3 | from riemann.script.opcodes import CODE_TO_INT, INT_TO_CODE 4 | 5 | 6 | def serialize(script_string: str) -> bytes: 7 | ''' 8 | Serialize a human-readable script to bytes 9 | 10 | Example: 11 | serialize('OP_DUP OP_CAT OP_HASH160 0011deadbeef') 12 | 13 | Args: 14 | script_string: A human-readable Bitcoin Script string 15 | Returns: 16 | The Script serialized as a bytestring 17 | ''' 18 | string_tokens = script_string.split() 19 | serialized_script = bytearray() 20 | 21 | for token in string_tokens: 22 | if token == 'OP_CODESEPARATOR' or token == 'OP_PUSHDATA4': 23 | raise NotImplementedError('{} is a bad idea.'.format(token)) 24 | 25 | if token in riemann.network.CODE_TO_INT_OVERWRITE: 26 | serialized_script.extend( 27 | [riemann.network.CODE_TO_INT_OVERWRITE[token]]) 28 | 29 | elif token in CODE_TO_INT: 30 | serialized_script.extend([CODE_TO_INT[token]]) 31 | 32 | else: 33 | token_bytes = bytes.fromhex(token) 34 | 35 | if len(token_bytes) <= 75: 36 | op = 'OP_PUSH_{}'.format(len(token_bytes)) 37 | serialized_script.extend([CODE_TO_INT[op]]) 38 | serialized_script.extend(token_bytes) 39 | 40 | elif len(token_bytes) > 75 and len(token_bytes) <= 255: 41 | op = 'OP_PUSHDATA1' 42 | serialized_script.extend([CODE_TO_INT[op]]) 43 | serialized_script.extend(utils.i2le(len(token_bytes))) 44 | serialized_script.extend(token_bytes) 45 | 46 | elif len(token_bytes) > 255 and len(token_bytes) <= 1000: 47 | op = 'OP_PUSHDATA2' 48 | serialized_script.extend([CODE_TO_INT[op]]) 49 | serialized_script.extend( 50 | utils.i2le_padded(len(token_bytes), 2)) 51 | serialized_script.extend(token_bytes) 52 | 53 | else: 54 | raise NotImplementedError( 55 | 'Hex string too long to serialize.') 56 | 57 | return serialized_script 58 | 59 | 60 | def hex_serialize(script_string: str) -> str: 61 | ''' 62 | Serialize a human-readable script to hex 63 | 64 | Example: 65 | hex_serialize('OP_DUP OP_CAT OP_HASH160 0011deadbeef') 66 | 67 | Args: 68 | script_string: A human-readable Bitcoin Script string 69 | Returns: 70 | The Script serialized as a hex string 71 | ''' 72 | return serialize(script_string).hex() 73 | 74 | 75 | def deserialize(serialized_script: bytes) -> str: 76 | ''' 77 | Deserialize a human-readable script from bytes 78 | 79 | Example: 80 | deserialize(b'\x19\x76\xa9\x88\xac') 81 | Args: 82 | serialized_script: The Script serialized as a bytestring 83 | Returns: 84 | A human-readable Script string 85 | ''' 86 | deserialized = [] 87 | i = 0 88 | while i < len(serialized_script): 89 | current_byte = serialized_script[i] 90 | if current_byte == 0xab: 91 | raise NotImplementedError('OP_CODESEPARATOR is a bad idea.') 92 | if current_byte <= 75 and current_byte != 0: 93 | 94 | deserialized.append( 95 | serialized_script[i + 1: i + 1 + current_byte].hex()) 96 | 97 | i += 1 + current_byte 98 | if i > len(serialized_script): 99 | raise IndexError( 100 | 'Push {} caused out of bounds exception.' 101 | .format(current_byte)) 102 | 103 | elif current_byte == 76: 104 | # next hex blob length 105 | blob_len = serialized_script[i + 1] 106 | 107 | deserialized.append( 108 | serialized_script[i + 2: i + 2 + blob_len].hex()) 109 | 110 | i += 2 + blob_len 111 | 112 | elif current_byte == 77: 113 | # next hex blob length 114 | blob_len = utils.le2i(serialized_script[i + 1: i + 3]) 115 | 116 | deserialized.append( 117 | serialized_script[i + 3: i + 3 + blob_len].hex()) 118 | 119 | i += 3 + blob_len 120 | 121 | elif current_byte == 78: 122 | raise NotImplementedError('OP_PUSHDATA4 is a bad idea.') 123 | 124 | else: 125 | if current_byte in riemann.network.INT_TO_CODE_OVERWRITE: 126 | deserialized.append( 127 | riemann.network.INT_TO_CODE_OVERWRITE[current_byte]) 128 | elif current_byte in INT_TO_CODE: 129 | deserialized.append(INT_TO_CODE[current_byte]) 130 | else: 131 | raise ValueError( 132 | 'Unsupported opcode. ' 133 | 'Got 0x%x' % serialized_script[i]) 134 | i += 1 135 | 136 | return ' '.join(deserialized) 137 | 138 | 139 | def hex_deserialize(script_hex: str) -> str: 140 | ''' 141 | Deserialize a human-readable script from hex 142 | 143 | Example: 144 | hex_deserialize('1976a988ac') 145 | Args: 146 | serialized_script: The Script serialized as a hex string 147 | Returns: 148 | A human-readable Script string 149 | ''' 150 | return deserialize(bytes.fromhex(script_hex)) 151 | -------------------------------------------------------------------------------- /riemann/tests/tx/test_shared.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | import unittest 3 | from riemann import tx 4 | 5 | 6 | class TestByteData(unittest.TestCase): 7 | 8 | def setUp(self): 9 | pass 10 | 11 | def test_iter(self): 12 | bd = tx.ByteData() 13 | bd._bytes.extend(b'\x00\x00') 14 | i = iter(bd) 15 | next(i) 16 | next(i) 17 | self.assertRaises(StopIteration, i.__next__) 18 | 19 | def test_iadd_error(self): 20 | bd = tx.ByteData() 21 | with self.assertRaises(TypeError) as context: 22 | bd += 'alphabet' 23 | 24 | self.assertIn('unsupported operand type(s) for +=: ' 25 | 'ByteData and str', str(context.exception)) 26 | 27 | def test_setattr_error(self): 28 | bd = tx.ByteData() 29 | bd._make_immutable() 30 | with self.assertRaises(TypeError) as context: 31 | bd.a = 'aaaaa' 32 | 33 | self.assertIn('cannot be written to.', str(context.exception)) 34 | 35 | def test_repr(self): 36 | bd = tx.ByteData() 37 | bd._bytes.extend(b'\xff') 38 | 39 | self.assertEqual(bd.__repr__(), "ByteData: bytearray(b'\\xff')") 40 | 41 | def test_find(self): 42 | bd = tx.ByteData() 43 | bd._bytes.extend(b'\xff\xdd\x88') 44 | 45 | self.assertEqual(bd.find(b'\xff'), 0) 46 | self.assertEqual(bd.find(b'\xdd'), 1) 47 | self.assertEqual(bd.find(b'\x88'), 2) 48 | self.assertEqual(bd.find(b'\x00'), -1) 49 | 50 | bd2 = tx.ByteData() 51 | bd2._bytes.extend(b'\xaa') 52 | 53 | self.assertEqual(bd.find(bd2), -1) 54 | 55 | def test_hex(self): 56 | t = b'\xff\xdd\x88' 57 | bd = tx.ByteData() 58 | bd._bytes.extend(t) 59 | 60 | self.assertEqual(bd.hex(), t.hex()) 61 | 62 | def test_ne_error(self): 63 | with self.assertRaises(TypeError) as context: 64 | bd = tx.ByteData() 65 | bd == 'hello world' 66 | 67 | self.assertIn( 68 | 'Equality not supported for ByteData and ', 69 | str(context.exception)) 70 | 71 | 72 | class TestVarInt(unittest.TestCase): 73 | 74 | def setUp(self): 75 | pass 76 | 77 | def tearDown(self): 78 | riemann.select_network('bitcoin_main') 79 | 80 | def test_one_byte(self): 81 | res = tx.VarInt(0xfb) 82 | self.assertEqual(res, b'\xfb') 83 | self.assertIsInstance(res, tx.VarInt) 84 | 85 | def test_one_byte_boundary(self): 86 | res = tx.VarInt(0xff) 87 | self.assertEqual(res, b'\xfd' + b'\xff\x00') 88 | self.assertIsInstance(res, tx.VarInt) 89 | 90 | def test_two_bytes(self): 91 | res = tx.VarInt(0xffff) 92 | self.assertEqual(res, b'\xfd' + (b'\xff' * 2)) 93 | self.assertIsInstance(res, tx.VarInt) 94 | 95 | def test_four_bytes(self): 96 | res = tx.VarInt(0xffffffff) 97 | self.assertEqual(res, b'\xfe' + (b'\xff' * 4)) 98 | self.assertIsInstance(res, tx.VarInt) 99 | 100 | def test_eight_bytes(self): 101 | res = tx.VarInt(0xffffffffffffffff) 102 | self.assertEqual(res, b'\xff' + (b'\xff' * 8)) 103 | self.assertIsInstance(res, tx.VarInt) 104 | 105 | res = tx.VarInt(0x0123456789abcdef) 106 | self.assertEqual(res, b'\xff' + b'\xef\xcd\xab\x89\x67\x45\x23\x01') 107 | 108 | res = tx.VarInt(0x234000000000) # 6 bytes to test padding 109 | self.assertEqual(res, b'\xff' + b'\x00\x00\x00\x00\x40\x23\x00\x00') 110 | 111 | def test_negative(self): 112 | with self.assertRaises(ValueError) as context: 113 | tx.VarInt(-5) 114 | 115 | self.assertIn('VarInt cannot be less than 0.', 116 | str(context.exception)) 117 | 118 | def test_too_high(self): 119 | with self.assertRaises(ValueError) as context: 120 | tx.VarInt(2 ** 64 + 1) 121 | 122 | self.assertIn('VarInt cannot be greater than (2 ** 64) - 1.', 123 | str(context.exception)) 124 | 125 | def test_copy(self): 126 | res = tx.VarInt(0xffffffffffffffff) 127 | copy = res.copy() 128 | self.assertEqual(res, copy) 129 | self.assertIsNot(res, copy) 130 | 131 | def test_from_bytes(self): 132 | 133 | # This test is kinda a joke 134 | self.assertEqual(tx.VarInt.from_bytes(b'\xfd\x91#'), b'\xfd\x91#') 135 | self.assertEqual(tx.VarInt.from_bytes(b'\x00'), b'\x00') 136 | self.assertEqual(tx.VarInt.from_bytes(b'\xff' * 9), b'\xff' * 9) 137 | 138 | with self.assertRaises(ValueError) as context: 139 | tx.VarInt.from_bytes(b'\xfe') 140 | self.assertIn( 141 | 'Malformed VarInt. Got: fe', 142 | str(context.exception)) 143 | 144 | with self.assertRaises(ValueError) as context: 145 | tx.VarInt.from_bytes(b'\xfe\x00\x00\x00') 146 | self.assertIn( 147 | 'Malformed VarInt. Got: fe', 148 | str(context.exception)) 149 | 150 | def test_zcash_compact_enforcement(self): 151 | riemann.select_network('zcash_overwinter_main') 152 | 153 | with self.assertRaises(ValueError) as context: 154 | tx.VarInt.from_bytes(b'\xfd\x00\x00') 155 | 156 | self.assertIn( 157 | 'VarInt must be compact. Got:', 158 | str(context.exception)) 159 | 160 | with self.assertRaises(ValueError) as context: 161 | tx.VarInt.from_bytes(b'\xfe\x00\x00\x00\x00') 162 | 163 | self.assertIn( 164 | 'VarInt must be compact. Got:', 165 | str(context.exception)) 166 | -------------------------------------------------------------------------------- /riemann/tx/zcash_shared.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | from riemann import utils 3 | from riemann.tx import shared 4 | 5 | 6 | class ZcashByteData(shared.ByteData): 7 | def __init__(self): 8 | if 'zcash' not in riemann.get_current_network_name(): 9 | raise ValueError('Zcash classes not supported by network {}. ' 10 | 'How did you get here?' 11 | .format(riemann.get_current_network_name())) 12 | super().__init__() 13 | 14 | 15 | class SproutZkproof(ZcashByteData): 16 | 17 | pi_sub_a: bytes 18 | pi_prime_sub_a: bytes 19 | pi_sub_b: bytes 20 | pi_prime_sub_b: bytes 21 | pi_sub_c: bytes 22 | pi_prime_sub_c: bytes 23 | pi_sub_k: bytes 24 | pi_sub_h: bytes 25 | 26 | def __init__(self, 27 | pi_sub_a: bytes, 28 | pi_prime_sub_a: bytes, 29 | pi_sub_b: bytes, 30 | pi_prime_sub_b: bytes, 31 | pi_sub_c: bytes, 32 | pi_prime_sub_c: bytes, 33 | pi_sub_k: bytes, 34 | pi_sub_h: bytes): 35 | super().__init__() 36 | 37 | self.validate_bytes(pi_sub_a, 33) 38 | self.validate_bytes(pi_prime_sub_a, 33) 39 | self.validate_bytes(pi_sub_b, 65) 40 | self.validate_bytes(pi_prime_sub_b, 33) 41 | self.validate_bytes(pi_sub_c, 33) 42 | self.validate_bytes(pi_prime_sub_c, 33) 43 | self.validate_bytes(pi_sub_k, 33) 44 | self.validate_bytes(pi_sub_h, 33) 45 | 46 | self += pi_sub_a 47 | self += pi_prime_sub_a 48 | self += pi_sub_b 49 | self += pi_prime_sub_b 50 | self += pi_sub_c 51 | self += pi_prime_sub_c 52 | self += pi_sub_k 53 | self += pi_sub_h 54 | 55 | self.pi_sub_a = pi_sub_a 56 | self.pi_prime_sub_a = pi_prime_sub_a 57 | self.pi_sub_b = pi_sub_b 58 | self.pi_prime_sub_b = pi_prime_sub_b 59 | self.pi_sub_c = pi_sub_c 60 | self.pi_prime_sub_c = pi_prime_sub_c 61 | self.pi_sub_k = pi_sub_k 62 | self.pi_sub_h = pi_sub_h 63 | 64 | self._make_immutable() 65 | 66 | @classmethod 67 | def from_bytes(SproutZkproof, byte_string: bytes) -> 'SproutZkproof': 68 | return SproutZkproof( 69 | pi_sub_a=byte_string[0:33], 70 | pi_prime_sub_a=byte_string[33:66], 71 | pi_sub_b=byte_string[66:131], 72 | pi_prime_sub_b=byte_string[131:164], 73 | pi_sub_c=byte_string[164:197], 74 | pi_prime_sub_c=byte_string[197:230], 75 | pi_sub_k=byte_string[230:263], 76 | pi_sub_h=byte_string[263:296]) 77 | 78 | 79 | class SproutJoinsplit(ZcashByteData): 80 | 81 | vpub_old: bytes 82 | vpub_new: bytes 83 | anchor: bytes 84 | nullifiers: bytes 85 | commitments: bytes 86 | ephemeral_key: bytes 87 | random_seed: bytes 88 | vmacs: bytes 89 | zkproof: SproutZkproof 90 | encoded_notes: bytes 91 | 92 | def __init__(self, 93 | vpub_old: bytes, 94 | vpub_new: bytes, 95 | anchor: bytes, 96 | nullifiers: bytes, 97 | commitments: bytes, 98 | ephemeral_key: bytes, 99 | random_seed: bytes, 100 | vmacs: bytes, 101 | zkproof: SproutZkproof, 102 | encoded_notes: bytes): 103 | super().__init__() 104 | 105 | if not isinstance(zkproof, SproutZkproof): 106 | raise ValueError( 107 | 'Invalid zkproof. ' 108 | 'Expected instance of SproutZkproof. Got {}' 109 | .format(type(zkproof).__name__)) 110 | if (utils.le2i(vpub_old) != 0 and utils.le2i(vpub_new) != 0): 111 | raise ValueError('vpub_old or vpub_new must be zero') 112 | 113 | self.validate_bytes(vpub_old, 8) 114 | self.validate_bytes(vpub_new, 8) 115 | self.validate_bytes(anchor, 32) 116 | self.validate_bytes(nullifiers, 64) 117 | self.validate_bytes(commitments, 64) 118 | self.validate_bytes(ephemeral_key, 32) 119 | self.validate_bytes(random_seed, 32) 120 | self.validate_bytes(vmacs, 64) 121 | self.validate_bytes(encoded_notes, 1202) 122 | 123 | self += vpub_old 124 | self += vpub_new 125 | self += anchor 126 | self += nullifiers 127 | self += commitments 128 | self += ephemeral_key 129 | self += random_seed 130 | self += vmacs 131 | self += zkproof 132 | self += encoded_notes 133 | 134 | self.vpub_old = vpub_old 135 | self.vpub_new = vpub_new 136 | self.anchor = anchor 137 | self.nullifiers = nullifiers 138 | self.commitments = commitments 139 | self.ephemeral_key = ephemeral_key 140 | self.random_seed = random_seed 141 | self.vmacs = vmacs 142 | self.zkproof = zkproof 143 | self.encoded_notes = encoded_notes 144 | 145 | self._make_immutable() 146 | 147 | @classmethod 148 | def from_bytes(SproutJoinsplit, byte_string: bytes) -> 'SproutJoinsplit': 149 | return SproutJoinsplit( 150 | vpub_old=byte_string[0:8], 151 | vpub_new=byte_string[8:16], 152 | anchor=byte_string[16:48], 153 | nullifiers=byte_string[48:112], 154 | commitments=byte_string[112:176], 155 | ephemeral_key=byte_string[176:208], 156 | random_seed=byte_string[208:240], 157 | vmacs=byte_string[240:304], 158 | zkproof=SproutZkproof.from_bytes(byte_string[304:600]), 159 | encoded_notes=byte_string[600:1802]) 160 | -------------------------------------------------------------------------------- /riemann/encoding/bech32.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2017 Pieter Wuille 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. 22 | 23 | '''Reference implementation for Bech32 and segwit addresses.''' 24 | 25 | import riemann 26 | 27 | 28 | CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 29 | 30 | 31 | def encode(data: bytes) -> str: 32 | '''Convert bytes to bech32''' 33 | if riemann.network.BECH32_HRP is None: 34 | raise ValueError( 35 | 'Network ({}) does not support bech32 encoding.' 36 | .format(riemann.get_current_network_name())) 37 | return segwit_encode(riemann.network.BECH32_HRP, data[0], data[2:]) 38 | 39 | 40 | def decode(bech: str) -> bytes: 41 | '''Convert bech32 to bytes''' 42 | if riemann.network.BECH32_HRP is None: 43 | raise ValueError( 44 | 'Network ({}) does not support bech32 encoding.' 45 | .format(riemann.get_current_network_name())) 46 | 47 | (version_prefix, hash_int_array) = \ 48 | segwit_decode(riemann.network.BECH32_HRP, bech) 49 | ret = bytearray() 50 | ret.extend([version_prefix]) 51 | ret.extend([len(hash_int_array)]) 52 | ret.extend(hash_int_array) 53 | return bytes(ret) 54 | 55 | 56 | def segwit_decode(hrp, addr): 57 | '''Decode a segwit address.''' 58 | hrpgot, data = bech32_decode(addr) 59 | if hrpgot != hrp: 60 | return (None, None) 61 | decoded = convertbits(data[1:], 5, 8, False) 62 | if decoded is None or len(decoded) < 2 or len(decoded) > 40: 63 | return (None, None) 64 | if data[0] > 16: 65 | return (None, None) 66 | if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: 67 | return (None, None) 68 | return (data[0], decoded) 69 | 70 | 71 | def segwit_encode(hrp, witver, witprog): 72 | '''Encode a segwit address.''' 73 | ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) 74 | if segwit_decode(hrp, ret) == (None, None): 75 | return None 76 | return ret 77 | 78 | 79 | def bech32_encode(hrp, data): 80 | '''Compute a Bech32 string given HRP and data values.''' 81 | combined = data + bech32_create_checksum(hrp, data) 82 | return hrp + '1' + ''.join([CHARSET[d] for d in combined]) 83 | 84 | 85 | def bech32_decode(bech): 86 | '''Validate a Bech32 string, and determine HRP and data.''' 87 | if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) 88 | or (bech.lower() != bech and bech.upper() != bech)): 89 | return (None, None) 90 | bech = bech.lower() 91 | pos = bech.rfind('1') 92 | if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: 93 | return (None, None) 94 | if not all(x in CHARSET for x in bech[pos + 1:]): 95 | return (None, None) 96 | hrp = bech[:pos] 97 | data = [CHARSET.find(x) for x in bech[pos + 1:]] 98 | if not bech32_verify_checksum(hrp, data): 99 | return (None, None) 100 | return (hrp, data[:-6]) 101 | 102 | 103 | def bech32_polymod(values): 104 | '''Internal function that computes the Bech32 checksum.''' 105 | generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] 106 | chk = 1 107 | for value in values: 108 | top = chk >> 25 109 | chk = (chk & 0x1ffffff) << 5 ^ value 110 | for i in range(5): 111 | chk ^= generator[i] if ((top >> i) & 1) else 0 112 | return chk 113 | 114 | 115 | def bech32_hrp_expand(hrp): 116 | '''Expand the HRP into values for checksum computation.''' 117 | return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] 118 | 119 | 120 | def bech32_verify_checksum(hrp, data): 121 | '''Verify a checksum given HRP and converted data characters.''' 122 | return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 123 | 124 | 125 | def bech32_create_checksum(hrp, data): 126 | '''Compute the checksum values given HRP and data.''' 127 | values = bech32_hrp_expand(hrp) + data 128 | polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 129 | return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] 130 | 131 | 132 | def convertbits(data, frombits, tobits, pad=True): 133 | '''General power-of-2 base conversion.''' 134 | acc = 0 135 | bits = 0 136 | ret = [] 137 | maxv = (1 << tobits) - 1 138 | max_acc = (1 << (frombits + tobits - 1)) - 1 139 | for value in data: 140 | if value < 0 or (value >> frombits): 141 | return None 142 | acc = ((acc << frombits) | value) & max_acc 143 | bits += frombits 144 | while bits >= tobits: 145 | bits -= tobits 146 | ret.append((acc >> bits) & maxv) 147 | if pad: 148 | if bits: 149 | ret.append((acc << (tobits - bits)) & maxv) 150 | elif bits >= frombits or ((acc << (tobits - bits)) & maxv): 151 | return None 152 | return ret 153 | -------------------------------------------------------------------------------- /riemann/networks/standard.py: -------------------------------------------------------------------------------- 1 | from riemann import tx 2 | from riemann import utils as rutils 3 | from riemann.encoding import addresses as addr 4 | from riemann.script import serialization as ser 5 | from riemann.script import opcodes 6 | 7 | MAX_STANDARD_TX_WEIGHT = 400000 8 | MIN_STANDARD_TX_NONWITNESS_SIZE = 82 9 | 10 | 11 | def check_is_standard_tx(t: tx.Tx) -> bool: 12 | ''' 13 | Analog of Bitcoin's IsStandardTx 14 | Args: 15 | t (tx.Tx): the transaction 16 | Returns: 17 | (bool): True for standard, false for non-standard 18 | ''' 19 | # 'version' 20 | if t.version[0] not in [1, 2]: 21 | return False 22 | 23 | # 'tx-size' 24 | if len(t.no_witness()) * 3 + len(t) > MAX_STANDARD_TX_WEIGHT: 25 | return False 26 | 27 | for tx_in in t.tx_ins: 28 | try: 29 | # 'scriptsig-size' 30 | # 'scriptsig-not-pushonly' 31 | if (len(tx_in.script_sig) > 1650 32 | or not is_push_only(tx_in.script_sig)): 33 | return False 34 | except Exception: 35 | return False 36 | 37 | # 'scriptpubkey' 38 | # 'dust' 39 | # 'multi-op-return' 40 | if not check_is_standard(t): 41 | return False 42 | 43 | return True 44 | 45 | 46 | def is_push_only(script_sig: bytes) -> bool: 47 | ''' 48 | Determines whether a script is push-only 49 | Does this by parsing, and inspecting non-data elements 50 | Args: 51 | script_sig (bytes): the scriptSig 52 | Returns: 53 | (bool): True if Push Only, otherwise False 54 | ''' 55 | script = ser.deserialize(script_sig) 56 | non_data_opcodes = [t for t in script if t[0:3] == 'OP_'] 57 | for token in non_data_opcodes: 58 | integer_opcode = opcodes.CODE_TO_INT[token] 59 | if (integer_opcode in [79, 80] 60 | or integer_opcode >= 97): 61 | return False 62 | return True 63 | 64 | 65 | def check_is_standard(t: tx.Tx) -> bool: 66 | ''' 67 | Analog of Bitcoin's IsStandard 68 | Args: 69 | t (tx.Tx): the transaction to check 70 | Returns: 71 | (bool): True for standard, false for non-standard 72 | ''' 73 | for o in t.tx_outs: 74 | # 'scriptpubkey' 75 | if not is_standard_output_type(o): 76 | return False 77 | 78 | # 'dust' 79 | if (rutils.le2i(o.value) < 550 80 | and o.output_script[:2] != b'\x00\x14'): 81 | return False 82 | 83 | # 'multi-op-return' 84 | if len([is_op_return(o) for o in t.tx_outs]) > 1: 85 | return False 86 | 87 | return True 88 | 89 | 90 | def is_op_return(o: tx.TxOut) -> bool: 91 | ''' 92 | Checks whether a txout is standard TX_NULL_DATA op_return output 93 | Args: 94 | o (tx.TxOut): the output 95 | Returns: 96 | (bool): True if standard opreturn, otherwise false 97 | ''' 98 | script: str = ser.deserialize(o.output_script) 99 | split_script = script.split() 100 | 101 | # TX_NULL_DATA, up to 83 bytes (80 for safety) 102 | if (rutils.le2i(o.value) == 0 103 | and split_script[0] == 'OP_RETURN' 104 | and len(script) < 80): 105 | return True 106 | return False 107 | 108 | 109 | def is_standard_output_type(o: tx.TxOut) -> bool: 110 | ''' 111 | Checks standardness of an output based on its value and output script 112 | Args: 113 | o (tx.TxOut): the output the check 114 | Returns: 115 | (bool): True if standard, False otherwise 116 | ''' 117 | # TX_SCRIPTHASH 118 | # TX_WITNESS_V0_KEYHASH 119 | # TX_WITNESS_V0_SCRIPTHASH 120 | # TX_PUBKEYHASH 121 | try: 122 | addr.from_output_script(o.output_script) 123 | return True 124 | except ValueError: 125 | pass 126 | 127 | script: str = ser.deserialize(o.output_script) 128 | split_script = script.split() 129 | 130 | # TX_PUBKEY 131 | if (split_script[-1] == 'OP_CHECKSIG' 132 | and len(split_script) == 2 133 | and len(bytes.fromhex(split_script[1])) in [33, 65]): 134 | return True 135 | 136 | # TX_MULTISIG, up to x-of-3 137 | if (split_script[-1] == 'OP_CHECKMULTISIG' 138 | and split_script[-2] in ['OP_1', 'OP_2', 'OP_3']): 139 | 140 | num_pubkeys = int(split_script[-2][-1]) 141 | num_sigs = int(split_script[0][-1]) 142 | 143 | if (num_sigs > num_pubkeys # 3-of-2, or 16-of-3, or something 144 | or len(split_script) != num_pubkeys + 3): # some junk script 145 | return False 146 | for pubkey in split_script[1:-2]: 147 | if len(bytes.fromhex(pubkey)) not in [33, 65]: 148 | return False 149 | return True 150 | 151 | # TX_NONSTANDARD/TX_WITNESS_UNKNOWN 152 | return False 153 | 154 | 155 | def check_tx_size_small(t: tx.Tx) -> bool: 156 | ''' 157 | Args: 158 | t (tx.Tx): the transaction 159 | Returns: 160 | (bool): True for standard, False for non-standard 161 | ''' 162 | return len(t.no_witness()) >= MIN_STANDARD_TX_NONWITNESS_SIZE 163 | 164 | 165 | def check_final( 166 | t: tx.Tx, 167 | best_height: int = 0, 168 | best_timestamp: int = 0) -> bool: 169 | ''' 170 | Checks absolute locktime of a transaction. 171 | Pass in the best height and timestamp 172 | Args: 173 | t (tx.Tx): the transaction 174 | best_height (int): best known Bitcoin height 175 | best_timestamp (int): best known Bitcoin timestamp 176 | ''' 177 | lock = rutils.le2i(t.lock_time) 178 | if lock >= 500_000_000: # timelocked 179 | return lock <= best_timestamp 180 | else: # height-locked 181 | return lock <= best_height 182 | 183 | 184 | def check_bip68_final(): 185 | ... 186 | 187 | 188 | def check_nonstandard_inputs(): 189 | ... 190 | 191 | 192 | def check_witness_nonstandard(): 193 | ... 194 | 195 | 196 | def check_too_many_sigops(): 197 | ... 198 | 199 | 200 | def check_non_mandatory_script(): 201 | ... 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Riemann: bitcoin transactions for humans 2 | 3 | [![Build Status](https://travis-ci.org/summa-tx/riemann.svg?branch=master)](https://travis-ci.org/summa-tx/riemann) 4 | [![Coverage Status](https://coveralls.io/repos/github/summa-tx/riemann/badge.svg)](https://coveralls.io/github/summa-tx/riemann) 5 | 6 | ### Purpose 7 | 8 | `$ pip install riemann-tx` 9 | 10 | Riemann is a **dependency-free Python3** library for creating **bitcoin-style 11 | transactions**. It is **compatible with many chains** and **supports SegWit**. 12 | 13 | Riemann aims to make it easy to create application-specific transactions. It 14 | serializes and unserializes scripts from human-readable strings. It contains 15 | a complete toolbox for transaction construction, as well as built-in support 16 | for ~20 live networks and ~40 testnet or regtest nets. 17 | 18 | Riemann is NOT a wallet. It does NOT handle keys or create signatures. 19 | Riemann is NOT a protocol or RPC implementation. Riemann does NOT communicate 20 | with anything. Ever. Riemann is NOT a Script VM. Riemann does NOT check the 21 | validity of your scriptsigs. 22 | 23 | Riemann is _almost_ stateless. Before calling functions, you select a 24 | network. A list of supported networks is in `riemann/networks/__init__.py`. 25 | Tests are made using on-chain transactions, primarily from Bitcoin. 26 | 27 | ### Contributing 28 | 29 | Please read CONTRIBUTING.md. 30 | 31 | ### Installation, Development & Running Tests 32 | 33 | Install from pypi for use in your project: 34 | ``` 35 | pip3 install riemann-tx 36 | ``` 37 | 38 | Install to develop Riemann: 39 | ``` 40 | $ git clone git@github.com:summa-tx/riemann.git 41 | $ cd riemann 42 | $ virtualenv -p python3 venv 43 | $ source venv/bin/activate 44 | $ pip install -r requirements-test.txt 45 | $ pip install -e . 46 | $ tox 47 | ``` 48 | 49 | ### Usage 50 | 51 | At a low level, Riemann deals in byte-like objects. However, it provides 52 | layers of abstractions on top of this. Notably, scripts are commonly 53 | expressed as strings. In script strings, data (like pubkeys) is expressed in 54 | unprefixed hex. For example, a P2PKH output script_pubkey might be expressed 55 | as follows: 56 | 57 | ```Python 58 | # Note that the PUSH0x14 for the pubkey is implied 59 | "OP_DUP OP_HASH160 00112233445566778899AABBCCDDEEFF00112233 OP_EQUALVERIFY 60 | OP_CHECKSIG" 61 | ``` 62 | 63 | `tx.tx` contains the data structures for the different pieces of a transaction. 64 | It deals in bytes and bytearrays. 65 | 66 | `tx.tx_builder` provides tools for constructing transactions. It accepts 67 | human-readable inputs, like ints and human readable script strings wherever 68 | possible, and returns serialized transactions. 69 | 70 | `simple` contains a simplified interface to the tx_builder. It accepts 71 | human-readable inputs. 72 | 73 | Bitcoin mainnet is the default network. Select a network as follows: 74 | 75 | ```Python 76 | import riemann 77 | riemann.select_network('network_name') 78 | ``` 79 | 80 | When relevant, segwit is enabled by passing `witness=True`. Example: 81 | `make_sh_address(script_string, witness=True)`. There are also convenience 82 | functions that provide the same functionality, e.g., 83 | `make_p2wsh_address(script_string)`. 84 | 85 | Data structures are IMMUTABLE. You can not (and definitely should not!) edit an 86 | instance of any of the underlying classes. Instead, make a new instance, or use 87 | the `copy` method. The `copy` method allows you to make a copy, and takes 88 | arguments to override any specific attribute. 89 | 90 | ### Notes and Bitcoin gotchas: 91 | 92 | * For convenience, we separate the script_sig into the stack_script and the 93 | redeem_script. For PKH spends, the redeem script MUST BE `b''`. 94 | 95 | * If there are any witnesses, all inputs must have a witness. The witness list 96 | MUST be the same size as the input list. 97 | 98 | * If all sequence numbers are set to max (0xFFFFFFFF), `lock_time` is 99 | disregarded by consensus rules. For this reason, 0xFFFFFFFE is the default 100 | sequence number in simple.py. 101 | 102 | * Relative lock-time signaling uses a **different time format** than absolute 103 | lock-time. See here: https://prestwi.ch/bitcoin-time-locks/ 104 | 105 | * Not all chains support OP_CHECKSEQUENCEVERIFY and relative lock-times 106 | (lookin' at you Zcash). 107 | 108 | * Replace-by-fee signaling is also communicated by sequence numbers. If any 109 | sequence number is 0xFFFFFFFD or lower, then RBF is enabled. RBF is _NOT_ a 110 | consensus feature. 111 | 112 | * `lock_time` and `sequence` use different encodings for time. 113 | 114 | ```Python 115 | # NB: 116 | # script_sig -> Goes in TxIn. 117 | # - Legacy only 118 | # - Contains initial stack (stack_script) 119 | # - Contains p2sh script (redeem_script) 120 | # - Contains pubkey/script revelation 121 | # stack_script -> Goes in script_sig 122 | # - Legacy only 123 | # - Contains script that makes initial stack 124 | # script_pubkey -> Goes in TxOut 125 | # - Also called pk_script, output_script 126 | # - P2PKH: OP_DUP OP_HASH160 PUSH14 {pkh} OP_EQUALVERIFY OP_CHECKSIG 127 | # - P2SH: OP_HASH160 {script_hash} OP_EQUAL 128 | # - P2WPKH: OP_0 PUSH0x14 {pkh} 129 | # - P2WSH: OP_0 PUSH0x20 {script_hash} 130 | # WitnessStackItem -> Goes in InputWitness 131 | # - Witness only 132 | # - Contains a length-prefixed stack item 133 | # InputWitness -> Goes in Witness 134 | # - A stack associated with a specific input 135 | # - If spending from p2wsh, the last item is a serialized script 136 | # - If spending from p2wpkh, consists of [signature, pubkey] 137 | ``` 138 | 139 | # LICENSE 140 | 141 | Riemann is released under the LGPL. 142 | Riemann contains some code released under MIT and ISC licenses. The appropriate 143 | license is included at the top of these files. 144 | 145 | In particular: 146 | * Base58 implementation from the excellent pycoin by Richard Kiss. 147 | [Link](https://github.com/richardkiss/pycoin) 148 | * Bech32 implementation from Pieter Wuille. 149 | [Link](https://github.com/sipa/bech32/tree/master/ref/python) 150 | * blake256 and blake2 implementation by Larry Bugbee. 151 | [Link](http://www.seanet.com/~bugbee/crypto/blake/) 152 | [Link](https://github.com/buggywhip/blake2_py) 153 | -------------------------------------------------------------------------------- /docs/py-modindex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Python Module Index — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 | 36 | 37 |

Python Module Index

38 | 39 |
40 | r 41 |
42 | 43 | 44 | 45 | 47 | 48 | 50 | 53 | 54 | 55 | 58 | 59 | 60 | 63 | 64 | 65 | 68 | 69 | 70 | 73 | 74 | 75 | 78 | 79 | 80 | 83 | 84 | 85 | 88 | 89 | 90 | 93 | 94 | 95 | 98 |
 
46 | r
51 | riemann 52 |
    56 | riemann.encoding.addresses 57 |
    61 | riemann.encoding.base58 62 |
    66 | riemann.encoding.bech32 67 |
    71 | riemann.encoding.cashaddr 72 |
    76 | riemann.networks.networks 77 |
    81 | riemann.script.serialization 82 |
    86 | riemann.simple 87 |
    91 | riemann.tx.tx_builder 92 |
    96 | riemann.utils 97 |
99 | 100 | 101 |
102 | 103 |
104 |
105 | 155 |
156 |
157 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/witnessstackitem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WitnessStackItem — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

WitnessStackItem

37 |
38 |
39 | class riemann.tx.WitnessStackItem(item: bytes)
40 |

A witness stack item. Each input witness is composed of an initial stack 41 | to be evaluated by the witness program. Witnesses for P2WSH inputs have a 42 | serialized script as the last stack element.

43 |
44 |
Parameters
45 |

item – the raw data to be placed on the stack

46 |
47 |
48 |
49 |
50 | item
51 |

the raw data to be placed on the stack

52 |
53 | 54 |
55 |
56 | classmethod from_bytes(byte_string: bytes) → riemann.tx.tx.WitnessStackItem
57 |

Parse a WitnessStackItem from a bytestring. Also available as from_hex

58 |
59 | 60 |
61 | 62 |
63 | 64 | 65 |
66 | 67 |
68 |
69 | 132 |
133 |
134 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /docs/transactions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Riemann Transactions — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

Riemann Transactions

37 |

Riemann supports transactions in many chains. See Networks for a 38 | complete list.

39 |

Riemann represents transactions as bytestrings with some additional sugar on 40 | top. All elements of a transaction inherit from the ByteData class. The 41 | ByteData class provides useful syntactic sugar:

42 |
# indexing
 43 | version = my_tx[:4]
 44 | 
 45 | # formatting
 46 | f'{my_tx:x}'
 47 | 
 48 | # equality
 49 | my_tx == some_other_tx
 50 | 
51 |
52 |

Generally, developers want to interact with the Tx class and its components, 53 | TxIn, TxOut, and InputWitness.

54 |
55 |

Tx Classes:

56 | 65 |
66 |
67 | 68 | 69 |
70 | 71 |
72 |
73 | 134 |
135 |
136 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /riemann/tests/encoding/test_bech32.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Copyright (c) 2017 Pieter Wuille 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. 22 | 23 | 24 | """Reference tests for segwit adresses""" 25 | 26 | import unittest 27 | import riemann 28 | from ...encoding import bech32 29 | 30 | 31 | def segwit_scriptpubkey(witver, witprog): 32 | """Construct a Segwit scriptPubKey for a given witness program.""" 33 | return bytes([witver + 0x50 if witver else 0, len(witprog)] + witprog) 34 | 35 | 36 | VALID_CHECKSUM = [ 37 | "A12UEL5L", 38 | "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", # noqa: E501 39 | "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", 40 | "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", # noqa: E501 41 | "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", 42 | ] 43 | 44 | 45 | INVALID_CHECKSUM = [ 46 | " 1nwldj5", 47 | "\x7F" + "1axkwrx", 48 | "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", # noqa: E501 49 | "pzry9x0s0muk", 50 | "1pzry9x0s0muk", 51 | "x1b4n0q5v", 52 | "li1dgmt3", 53 | "de1lg7wt\xff", 54 | ] 55 | 56 | 57 | VALID_ADDRESS = [ 58 | ["BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6"], # noqa: E501 59 | ["tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", 60 | "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"], 61 | ["bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", # noqa: E501 62 | "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"], # noqa: E501 63 | ["BC1SW50QA3JX3S", "6002751e"], 64 | ["bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323"], # noqa: E501 65 | ["tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", 66 | "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"], 67 | ] 68 | 69 | INVALID_ADDRESS = [ 70 | "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", 71 | "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", 72 | "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", 73 | "bc1rw5uspcuh", 74 | "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", # noqa: E501 75 | "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", 76 | "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", 77 | "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", 78 | "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", 79 | "bc1gmk9yu", 80 | 81 | ] 82 | 83 | INVALID_ADDRESS_ENC = [ 84 | ("BC", 0, 20), 85 | ("bc", 0, 21), 86 | ("bc", 17, 32), 87 | ("bc", 1, 1), 88 | ("bc", 16, 41), 89 | ] 90 | 91 | 92 | class TestBech32(unittest.TestCase): 93 | """Unit test class for segwit addressess.""" 94 | 95 | def tearDown(self): 96 | riemann.select_network('bitcoin_main') 97 | 98 | def test_valid_checksum(self): 99 | """Test checksum creation and validation.""" 100 | for t in VALID_CHECKSUM: 101 | hrp, _ = bech32.bech32_decode(t) 102 | self.assertIsNotNone(hrp) 103 | pos = t.rfind('1') 104 | t = t[:pos + 1] + chr(ord(t[pos + 1]) ^ 1) + t[pos + 2:] 105 | hrp, _ = bech32.bech32_decode(t) 106 | self.assertIsNone(hrp) 107 | 108 | def test_invalid_checksum(self): 109 | """Test validation of invalid checksums.""" 110 | for test in INVALID_CHECKSUM: 111 | hrp, _ = bech32.bech32_decode(test) 112 | self.assertIsNone(hrp) 113 | 114 | def test_valid_address(self): 115 | """Test whether valid addresses decode to the correct output.""" 116 | for (address, hexscript) in VALID_ADDRESS: 117 | hrp = "bc" 118 | witver, witprog = bech32.segwit_decode(hrp, address) 119 | if witver is None: 120 | hrp = "tb" 121 | witver, witprog = bech32.segwit_decode(hrp, address) 122 | self.assertIsNotNone(witver) 123 | scriptpubkey = segwit_scriptpubkey(witver, witprog) 124 | self.assertEqual(scriptpubkey, bytearray.fromhex(hexscript)) 125 | addr = bech32.segwit_encode(hrp, witver, witprog) 126 | self.assertEqual(address.lower(), addr) 127 | 128 | def test_invalid_address(self): 129 | """Test whether invalid addresses fail to segwit_decode.""" 130 | for test in INVALID_ADDRESS: 131 | witver, _ = bech32.segwit_decode("bc", test) 132 | self.assertIsNone(witver) 133 | witver, _ = bech32.segwit_decode("tb", test) 134 | self.assertIsNone(witver) 135 | 136 | def test_invalid_address_enc(self): 137 | """Test whether address encoding fails on invalid input.""" 138 | for hrp, version, length in INVALID_ADDRESS_ENC: 139 | code = bech32.segwit_encode(hrp, version, [0] * length) 140 | self.assertIsNone(code) 141 | 142 | def test_encode_error(self): 143 | riemann.select_network('zcash_sprout_main') 144 | with self.assertRaises(ValueError) as context: 145 | bech32.encode(bytearray([0] * 32)) 146 | 147 | self.assertIn( 148 | 'Network (zcash_sprout_main) does not support bech32 encoding.', 149 | str(context.exception)) 150 | 151 | def test_decode_error(self): 152 | riemann.select_network('zcash_sprout_main') 153 | with self.assertRaises(ValueError) as context: 154 | bech32.decode(bytearray([0] * 32)) 155 | 156 | self.assertIn( 157 | 'Network (zcash_sprout_main) does not support bech32 encoding.', 158 | str(context.exception)) 159 | 160 | def test_convert_bits_error(self): 161 | self.assertIsNone(bech32.convertbits([2 ** 5 + 1], 5, 8)) 162 | -------------------------------------------------------------------------------- /riemann/tests/tx/test_tx_builder.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import riemann 3 | from unittest import mock 4 | from riemann.tests import helpers 5 | from riemann.tx import tx_builder as tb 6 | 7 | 8 | class TestTxBuilder(unittest.TestCase): 9 | 10 | def setUp(self): 11 | pass 12 | 13 | def tearDown(self): 14 | riemann.select_network('bitcoin_main') 15 | 16 | def test_make_sh_output_script(self): 17 | self.assertEqual( 18 | tb.make_sh_output_script('OP_IF'), 19 | helpers.OP_IF['output_script']) 20 | self.assertEqual( 21 | tb.make_sh_output_script( 22 | helpers.P2WSH['human']['witnesses'][0]['wit_script'], 23 | witness=True), 24 | helpers.P2WSH['ser']['ins'][0]['pk_script']) 25 | 26 | riemann.select_network('bitcoin_cash_main') 27 | with self.assertRaises(ValueError) as context: 28 | tb.make_sh_output_script( 29 | helpers.P2WSH['human']['witnesses'][0]['wit_script'], 30 | witness=True) 31 | 32 | self.assertIn( 33 | 'Network bitcoin_cash_main does not support witness scripts.', 34 | str(context.exception)) 35 | 36 | def test_make_pkh_output_script(self): 37 | self.assertEqual( 38 | tb.make_pkh_output_script(helpers.PK['ser'][0]['pk']), 39 | helpers.PK['ser'][0]['pkh_output']) 40 | self.assertEqual( 41 | tb.make_pkh_output_script( 42 | helpers.PK['ser'][0]['pk'], 43 | witness=True), 44 | helpers.PK['ser'][0]['pkh_p2wpkh_output']) 45 | 46 | riemann.select_network('bitcoin_cash_main') 47 | with self.assertRaises(ValueError) as context: 48 | tb.make_pkh_output_script(helpers.PK['ser'][0]['pk'], witness=True) 49 | 50 | self.assertIn( 51 | 'Network bitcoin_cash_main does not support witness scripts.', 52 | str(context.exception)) 53 | 54 | with self.assertRaises(ValueError) as context: 55 | tb.make_pkh_output_script('hello world') 56 | self.assertIn( 57 | 'Unknown pubkey format. Expected bytes. Got: ', 58 | str(context.exception)) 59 | 60 | def test_make_p2sh_output_script(self): 61 | self.assertEqual( 62 | tb.make_p2sh_output_script('OP_IF'), 63 | helpers.OP_IF['output_script']) 64 | 65 | def test_make_p2pkh_output_script(self): 66 | self.assertEqual( 67 | tb.make_p2pkh_output_script(helpers.PK['ser'][0]['pk']), 68 | helpers.PK['ser'][0]['pkh_output']) 69 | 70 | def test_make_p2wsh_output_script(self): 71 | self.assertEqual( 72 | tb.make_p2wsh_output_script( 73 | helpers.P2WSH['human']['witnesses'][0]['wit_script']), 74 | helpers.P2WSH['ser']['ins'][0]['pk_script']) 75 | 76 | def test_make_p2wpkh_output_script(self): 77 | self.assertEqual( 78 | tb.make_p2wpkh_output_script(helpers.PK['ser'][0]['pk']), 79 | helpers.PK['ser'][0]['pkh_p2wpkh_output']) 80 | 81 | def test_make_sh_output(self): 82 | pass # covered by next two 83 | 84 | def test_make_p2sh_output(self): 85 | self.assertEqual( 86 | tb.make_p2sh_output( 87 | value=helpers.P2PKH1['human']['outs'][0]['value'], 88 | output_script='OP_IF'), 89 | helpers.OP_IF['output']) 90 | 91 | def test_make_p2wsh_output(self): 92 | helper_witness = helpers.P2WSH['human']['witnesses'][0] 93 | self.assertEqual( 94 | tb.make_p2wsh_output( 95 | value=helpers.P2WSH['human']['outs'][3]['value'], 96 | output_script=helper_witness['wit_script']), 97 | helpers.P2WSH['ser']['outs'][3]['output']) 98 | 99 | def test_make_pkh_output(self): 100 | pass # covered by next 2 101 | 102 | def test_make_p2pkh_output(self): 103 | self.assertEqual( 104 | tb.make_p2pkh_output( 105 | value=helpers.P2PKH1['human']['outs'][0]['value'], 106 | pubkey=helpers.PK['ser'][0]['pk']), 107 | helpers.PK['ser'][0]['pk_p2pkh_output']) 108 | 109 | def test_make_p2wpkh_output(self): 110 | self.assertEqual( 111 | tb.make_p2wpkh_output( 112 | value=helpers.P2PKH1['human']['outs'][0]['value'], 113 | pubkey=helpers.PK['ser'][0]['pk']), 114 | helpers.PK['ser'][0]['pk_p2wpkh_output']) 115 | 116 | def test_make_op_return_output_error(self): 117 | with self.assertRaises(ValueError) as context: 118 | tb.make_op_return_output(b'\x00' * 78) 119 | 120 | self.assertIn( 121 | 'Data is too long. Expected <= 77 bytes', 122 | str(context.exception)) 123 | 124 | def test_make_empty_witness(self): 125 | pass 126 | 127 | def test_make_witness_stack_item(self): 128 | pass 129 | 130 | def test_make_witness(self): 131 | pass 132 | 133 | def test_make_outpoint(self): 134 | outpoint = tb.make_outpoint( 135 | tx_id_le=helpers.P2PKH1['ser']['ins'][0]['hash'], 136 | index=helpers.P2PKH1['human']['ins'][0]['index']) 137 | 138 | self.assertEqual( 139 | outpoint, 140 | helpers.P2PKH1['ser']['ins'][0]['outpoint']) 141 | 142 | def test_make_script_sig(self): 143 | tx_in = helpers.P2SH_PD1['human']['ins'][0] 144 | self.assertEqual( 145 | tb.make_script_sig( 146 | stack_script=tx_in['stack_script'], 147 | redeem_script=tx_in['redeem_script']), 148 | helpers.P2SH_PD1['ser']['ins'][0]['script_sig']) 149 | 150 | def test_make_legacy_input(self): 151 | outpoint = tb.make_outpoint( 152 | tx_id_le=helpers.P2PKH1['ser']['ins'][0]['hash'], 153 | index=helpers.P2PKH1['human']['ins'][0]['index']) 154 | 155 | tx_in = tb.make_legacy_input( 156 | outpoint=outpoint, 157 | stack_script=helpers.P2PKH1['ser']['ins'][0]['stack_script'], 158 | redeem_script=helpers.P2PKH1['ser']['ins'][0]['redeem_script'], 159 | sequence=helpers.P2PKH1['human']['ins'][0]['sequence']) 160 | 161 | self.assertEqual(tx_in, helpers.P2PKH1['ser']['tx']['in']) 162 | 163 | def test_make_legacy_input_and_empty_witness(self): 164 | pass 165 | 166 | def test_make_witness_input_and_witness(self): 167 | pass 168 | 169 | def test_length_prepend(self): 170 | self.assertEqual( 171 | tb.length_prepend(b'\x00'), 172 | b'\x01\x00') 173 | self.assertEqual( 174 | tb.length_prepend(b'\x00' * 99), 175 | b'\x63' + b'\x00' * 99) 176 | self.assertEqual( 177 | tb.length_prepend(b'\x00' * 256), 178 | b'\xfd\x00\x01' + b'\x00' * 256) 179 | 180 | @mock.patch('riemann.tx.tx_builder.sprout') 181 | def test_sprout_snowflake(self, mock_tx): 182 | # TODO: Improve 183 | riemann.select_network('zcash_sprout_main') 184 | mock_tx.SproutTx.return_value = 0 185 | self.assertEqual( 186 | tb.make_tx(0, 0, 0, 0, tx_joinsplits=[]), 187 | 0) 188 | 189 | @mock.patch('riemann.tx.tx_builder.overwinter') 190 | def test_overwinter_snowflake(self, mock_tx): 191 | # TODO: Improve 192 | riemann.select_network('zcash_overwinter_main') 193 | mock_tx.OverwinterTx.return_value = 0 194 | self.assertEqual( 195 | tb.make_tx(0, 0, 0, 0, expiry=0), 196 | 0) 197 | -------------------------------------------------------------------------------- /riemann/tests/encoding/test_addresses.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import riemann 3 | from riemann import utils 4 | from .. import helpers 5 | from ...encoding import addresses as addr 6 | 7 | # NB: 8 | # How to make more of these. 9 | # 1. Install python-bitcoinlib 10 | # 2. Compile a script to an array of ints (same as input to bytes class) 11 | # 3. Follow procedure below 12 | # 13 | # from bitcoin.core.script import CScript 14 | # from bitcoin.wallet import CBitcoinAddress 15 | # a = CScript(bytes.fromhex('HEX SCRIPT')) 16 | # CBitcoinAddress.from_scriptPubKey(a.to_p2sh_scriptPubKey() 17 | # 18 | # For more P2PKH addresses: 19 | # P2PKHBitcoinAddress.from_pubkey(PUBKEY_BYTES, accept_invalid=True) 20 | 21 | 22 | class TestAddresses(unittest.TestCase): 23 | 24 | def tearDown(self): 25 | riemann.select_network('bitcoin_main') 26 | 27 | def test_make_p2sh_address(self): 28 | a = addr.make_p2sh_address('OP_IF') 29 | self.assertEqual(a, helpers.OP_IF['p2sh']) 30 | 31 | def test_make_p2sh_address_msig(self): 32 | a = addr.make_p2sh_address(helpers.MSIG_2_2['redeem_script']) 33 | self.assertEqual(a, helpers.MSIG_2_2['p2sh']) 34 | 35 | def test_make_p2wsh_address(self): 36 | a = addr.make_p2wsh_address( 37 | helpers.P2WSH['human']['witnesses'][0]['wit_script']) 38 | self.assertEqual(a, helpers.P2WSH['human']['ins'][0]['addr']) 39 | 40 | def test_make_p2pkh_address(self): 41 | a = addr.make_p2pkh_address(b'\x00' * 65) 42 | self.assertEqual(a, helpers.ADDR[0]['p2pkh']) 43 | b = addr.make_p2pkh_address(b'\x11' * 65) 44 | self.assertEqual(b, helpers.ADDR[1]['p2pkh']) 45 | 46 | def test_make_p2wpkh_address(self): 47 | a = addr.make_p2wpkh_address(helpers.P2WPKH_ADDR['pubkey']) 48 | self.assertEqual(a, helpers.P2WPKH_ADDR['address']) 49 | 50 | def test_parse(self): 51 | self.assertEqual(addr.parse(helpers.OP_IF['p2sh']), 52 | b'\x05' + helpers.OP_IF['script_hash']) 53 | self.assertEqual(addr.parse(helpers.MSIG_2_2['p2sh']), 54 | b'\x05' + helpers.MSIG_2_2['script_hash']) 55 | self.assertEqual(addr.parse( 56 | helpers.P2WSH['human']['ins'][0]['addr']), 57 | b'\x00\x20' + helpers.P2WSH['ser']['ins'][0]['pk_script'][2:]) 58 | self.assertEqual(addr.parse(helpers.P2WPKH_ADDR['address']), 59 | b'\x00\x14' + helpers.P2WPKH_ADDR['pkh']) 60 | self.assertEqual(addr.parse(helpers.ADDR[0]['p2pkh']), 61 | b'\x00' + helpers.PK['ser'][0]['pkh']) 62 | 63 | with self.assertRaises(ValueError) as context: 64 | addr.parse('This is not a valid address.') 65 | 66 | self.assertIn('Unsupported address format. Got: ', 67 | str(context.exception)) 68 | 69 | def test_parse_hash(self): 70 | self.assertEqual(addr.parse_hash(helpers.OP_IF['p2sh']), 71 | helpers.OP_IF['script_hash']) 72 | self.assertEqual(addr.parse_hash(helpers.MSIG_2_2['p2sh']), 73 | helpers.MSIG_2_2['script_hash']) 74 | self.assertEqual( 75 | addr.parse_hash( 76 | helpers.P2WSH['human']['ins'][0]['addr']), 77 | helpers.P2WSH['ser']['ins'][0]['pk_script'][2:]) 78 | self.assertEqual(addr.parse_hash(helpers.P2WPKH_ADDR['address']), 79 | helpers.P2WPKH_ADDR['pkh']) 80 | self.assertEqual(addr.parse_hash(helpers.ADDR[0]['p2pkh']), 81 | helpers.PK['ser'][0]['pkh']) 82 | 83 | with self.assertRaises(ValueError) as context: 84 | addr.parse('bc1blahblahblah') 85 | 86 | self.assertIn('Unsupported address format. Got: ', 87 | str(context.exception)) 88 | 89 | # Test cash addr code 90 | riemann.select_network('bitcoin_cash_main') 91 | self.assertEqual( 92 | addr.parse_hash(helpers.OP_IF['p2sh']), 93 | helpers.OP_IF['script_hash']) 94 | 95 | self.assertEqual( 96 | addr.parse_hash(helpers.OP_IF['cashaddr']), 97 | helpers.OP_IF['script_hash']) 98 | 99 | self.assertEqual( 100 | addr.parse_hash(helpers.CASHADDR['p2pkh']), 101 | utils.hash160(helpers.CASHADDR['pubkey'])) 102 | 103 | def test_cashaddrs(self): 104 | riemann.select_network('bitcoin_cash_main') 105 | 106 | self.assertEqual( 107 | addr.make_legacy_p2sh_address('OP_IF'), 108 | helpers.OP_IF['p2sh']) 109 | 110 | self.assertEqual( 111 | addr.make_sh_address('OP_IF'), 112 | helpers.OP_IF['cashaddr']) 113 | 114 | self.assertEqual( 115 | addr.make_legacy_p2pkh_address(helpers.CASHADDR['pubkey']), 116 | helpers.CASHADDR['legacy_p2pkh']) 117 | 118 | self.assertEqual( 119 | addr.make_pkh_address(helpers.CASHADDR['pubkey']), 120 | helpers.CASHADDR['p2pkh']) 121 | 122 | def test_from_output_script(self): 123 | 124 | self.assertEqual( 125 | addr.from_output_script(helpers.OP_IF['output_script']), 126 | helpers.OP_IF['p2sh']) 127 | self.assertEqual( 128 | addr.from_output_script( 129 | helpers.P2WSH['ser']['ins'][0]['pk_script']), 130 | helpers.P2WSH['human']['ins'][0]['addr']) 131 | self.assertEqual( 132 | addr.from_output_script(helpers.PK['ser'][0]['pkh_output']), 133 | helpers.ADDR[0]['p2pkh']) 134 | self.assertEqual( 135 | addr.from_output_script(helpers.P2WPKH_ADDR['output']), 136 | helpers.P2WPKH_ADDR['address']) 137 | 138 | with self.assertRaises(ValueError) as context: 139 | addr.from_output_script(b'\x8e' * 34) 140 | self.assertIn( 141 | 'Cannot parse address from script.', 142 | str(context.exception)) 143 | 144 | def test_cashaddr_from_output_script(self): 145 | riemann.select_network('bitcoin_cash_main') 146 | self.assertEqual( 147 | addr.from_output_script(helpers.PK['ser'][0]['pkh_output']), 148 | helpers.ADDR[0]['p2pkh_cashaddr']) 149 | self.assertEqual( 150 | addr.from_output_script(helpers.OP_IF['output_script']), 151 | helpers.OP_IF['cashaddr']) 152 | 153 | def test_to_output_script(self): 154 | self.assertEqual( 155 | addr.to_output_script(helpers.OP_IF['p2sh']), 156 | helpers.OP_IF['output_script']) 157 | self.assertEqual( 158 | addr.to_output_script(helpers.P2WSH['human']['ins'][0]['addr']), 159 | helpers.P2WSH['ser']['ins'][0]['pk_script']) 160 | self.assertEqual( 161 | addr.to_output_script(helpers.ADDR[0]['p2pkh']), 162 | helpers.PK['ser'][0]['pkh_output']) 163 | self.assertEqual( 164 | addr.to_output_script(helpers.P2WPKH_ADDR['address']), 165 | helpers.P2WPKH_ADDR['output']) 166 | 167 | with self.assertRaises(ValueError) as context: 168 | # Junk B58 w valid checksum 169 | addr.to_output_script('1111111111111111111111111111111111177fdsQ') 170 | 171 | self.assertIn( 172 | 'Cannot parse output script from address.', 173 | str(context.exception)) 174 | 175 | def test_cashaddr_to_output_script(self): 176 | riemann.select_network('bitcoin_cash_main') 177 | 178 | self.assertEqual( 179 | addr.to_output_script(helpers.OP_IF['cashaddr']), 180 | helpers.OP_IF['output_script']) 181 | 182 | self.assertEqual( 183 | addr.to_output_script(helpers.ADDR[0]['p2pkh_cashaddr']), 184 | helpers.PK['ser'][0]['pkh_output']) 185 | -------------------------------------------------------------------------------- /docs/txout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TxOut — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

TxOut

37 |
38 |
39 | class riemann.tx.TxOut(value: bytes, output_script: bytes)
40 |

A transaction output, composed of a value (in satoshi) and an output script 41 | describing the spend conditions on the new UTXO.

42 |

Value is serialized as an 8-byte LE integer, measured in satoshi. Use the 43 | i2le_padded function in Utils to serialize integers.

44 |

TxOut accepts un-prepended output scripts, and adds their length for you.

45 |
46 |
Parameters
47 |
    48 |
  • value – the 8-byte LE encoded value of the output (in satoshi)

  • 49 |
  • output_script – the non-length-prepended output script as a bytestring

  • 50 |
51 |
52 |
53 |
54 |
55 | value
56 |

the 8-byte LE encoded value of the output (in satoshi)

57 |
58 | 59 |
60 |
61 | output_script
62 |

the non-length-prepended output script as a bytestring

63 |
64 | 65 |
66 |
67 | copy(value: Optional[bytes] = None, output_script: Optional[bytes] = None) → riemann.tx.tx.TxOut
68 |

Make a new copy of the object with optional modifications.

69 |
70 | 71 |
72 |
73 | classmethod from_bytes(byte_string: bytes) → riemann.tx.tx.TxOut
74 |

Parse a TxOut from a bytestring. Also available as from_hex

75 |
76 | 77 |
78 | 79 |
80 | 81 | 82 |
83 | 84 |
85 |
86 | 149 |
150 |
151 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/inputwitness.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | InputWitness — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

InputWitness

37 |

An InputWitness is the SegWit datastructure containing spend authorization 38 | for a specific input. It consists of a tuple (the stack) of 39 | WitnessStackItem objects.

40 |

Within a Tx the list of InputWitness objects is called tx_witnesses. Each 41 | witness and each item in its stack can be indexed by its position, e.g. 42 | my_tx.tx_witnesses[0].stack[4]. In the case of p2wsh inputs, the last stack 43 | item my_input_witness.stack[-1] is called the Witness Script, and contains a 44 | serialized Script program.

45 |
46 |
47 | class riemann.tx.InputWitness(stack: Sequence[riemann.tx.tx.WitnessStackItem])
48 |

The witness for a Compatibility or Segwit TxIn. It consists of a stack that 49 | will be evaluated by the witness program, represented as an ordered list of 50 | WitnessStackItem objects.

51 |
52 |
Parameters
53 |

stack – the ordered sequence of WitnessStackItems

54 |
55 |
56 |
57 |
58 | stack
59 |

the ordered sequence of WitnessStackItems

60 |
61 | 62 |
63 |
64 | copy(stack: Optional[List[riemann.tx.tx.WitnessStackItem]] = None) → riemann.tx.tx.InputWitness
65 |

Make a new copy of the object with optional modifications.

66 |
67 | 68 |
69 |
70 | classmethod from_bytes(byte_string: bytes) → riemann.tx.tx.InputWitness
71 |

Parse an InputWitness from a bytestring. Also available as from_hex

72 |
73 | 74 |
75 | 76 |
77 | 78 | 79 |
80 | 81 |
82 |
83 | 146 |
147 |
148 | 159 | 160 | 161 | 162 | 163 | 164 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Riemann: Cryptocurrency Transactions for Humans — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
30 | 31 | 32 |
33 | 34 |
35 |

Riemann: Cryptocurrency Transactions for Humans

36 |
$ pip install riemann-tx
 37 | 
38 |
39 |

Riemann is a dependency-free Python3 library for creating bitcoin-style 40 | transactions. It is compatible with many chains and supports SegWit.

41 |

Riemann aims to make it easy to create application-specific transactions. It 42 | serializes and unserializes scripts from human-readable strings. It contains 43 | a complete toolbox for transaction construction, as well as built-in support 44 | for ~20 live networks and ~40 testnet or regtest nets.

45 |

Riemann is NOT a wallet. It does NOT handle keys or create signatures. 46 | Riemann is NOT a protocol or RPC implementation. Riemann does NOT communicate 47 | with anything. Ever. Riemann is NOT a Script VM. Riemann does NOT check the 48 | validity of your scriptsigs.

49 |

Riemann is _almost_ stateless. Before calling functions, you select from a list 50 | of Networks. Tests are made using on-chain transactions, primarily from 51 | Bitcoin.

52 |
53 |

Submodules:

54 | 82 |
83 |
84 |
85 |

Indices and tables

86 | 91 |
92 | 93 | 94 |
95 | 96 |
97 |
98 | 149 |
150 |
151 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /docs/outpoint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Outpoint — riemann v2.0.3 documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |

Outpoint

37 |
38 |
39 | class riemann.tx.Outpoint(tx_id: bytes, index: bytes)
40 |

An outpoint. A pointer to the previous output being consumed by the 41 | associated input. It specifies the prevout by transaction id and index 42 | within that transaction’s output vector

43 |

NB: Args must be little-endian

44 |
45 |
Parameters
46 |
    47 |
  • tx_id – the 32-byte LE hash of the previous transaction

  • 48 |
  • index – the 4-byte LE encoded index of the prevout in its transaction

  • 49 |
50 |
51 |
52 |
53 |
54 | tx_id
55 |

the 32-byte LE hash of the previous transaction

56 |
57 | 58 |
59 |
60 | index
61 |

the 4-byte LE encoded index of the prevout in its transaction

62 |
63 | 64 |
65 |
66 | copy(tx_id: Optional[bytes] = None, index: Optional[bytes] = None) → riemann.tx.tx.Outpoint
67 |

Make a new copy of the object with optional modifications.

68 |
69 | 70 |
71 |
72 | classmethod from_bytes(byte_string: bytes) → riemann.tx.tx.Outpoint
73 |

Parse an Outpoint from a bytestring. Also available as from_hex

74 |
75 | 76 |
77 |
78 | classmethod null() → riemann.tx.tx.Outpoint
79 |

Make a null outpoing, as found in coinbase transactions

80 |
81 | 82 |
83 | 84 |
85 | 86 | 87 |
88 | 89 |
90 |
91 | 154 |
155 |
156 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /riemann/tx/shared.py: -------------------------------------------------------------------------------- 1 | import riemann 2 | from riemann import utils 3 | 4 | from typing import Any, Optional, Union 5 | Byteslike = Union[bytes, bytearray, 'ByteData'] 6 | 7 | SIGHASH_ALL = 0x01 8 | SIGHASH_NONE = 0x02 9 | SIGHASH_SINGLE = 0x03 10 | SIGHASH_FORKID = 0x40 11 | SIGHASH_ANYONECANPAY = 0x80 12 | 13 | 14 | class ByteData(): 15 | ''' 16 | Wrapper class for byte-like data 17 | Iterable and subscriptable (by iterating and subscribing to wrapped data) 18 | Can be made immutable 19 | self._bytes is a bytearray object when mutable 20 | self._bytes is a byte object when immutable 21 | Should be mostly transparent to the user 22 | Can be treated like bytes or a bytearray in most cases 23 | ''' 24 | __immutable = False 25 | 26 | def __init__(self): 27 | self._bytes = bytearray() 28 | 29 | def __iter__(self): 30 | return iter(self._bytes) 31 | 32 | def __getitem__(self, val): 33 | return self._bytes[val] 34 | 35 | def __iadd__(self, other: Byteslike): 36 | ''' 37 | ByteData, byte-like -> ByteData 38 | Define += operator. 39 | Extend self's bytes with other's bytes. 40 | ''' 41 | if isinstance(other, bytes) or isinstance(other, bytearray): 42 | self._bytes.extend(other) 43 | elif isinstance(other, ByteData): 44 | self._bytes.extend(other._bytes) 45 | else: 46 | raise TypeError('unsupported operand type(s) for +=: ' 47 | '{} and {}'.format(type(self).__name__, 48 | type(other).__name__)) 49 | return self 50 | 51 | def __ne__(self, other): 52 | ''' 53 | ByteData, byte-like -> bool 54 | Define != operator. 55 | Compares self._bytes to other. 56 | ''' 57 | if isinstance(other, bytes) or isinstance(other, bytearray): 58 | return self._bytes != other 59 | elif isinstance(other, ByteData): 60 | return self._bytes != other._bytes 61 | else: 62 | raise TypeError('Equality not supported for ByteData and {}.' 63 | .format(type(other))) 64 | 65 | def __eq__(self, other): 66 | ''' 67 | ByteData, byte-like -> bool 68 | Define == operator. 69 | ''' 70 | return not self != other 71 | 72 | def __len__(self): 73 | ''' 74 | ByteData -> int 75 | ''' 76 | return len(self._bytes) 77 | 78 | def __setattr__(self, key: str, value): 79 | if self.__immutable: 80 | raise TypeError("%r cannot be written to." % self) 81 | object.__setattr__(self, key, value) 82 | 83 | def __format__(self, code): 84 | ''' 85 | ByteData -> str 86 | us use the internal bytes formatting methods 87 | ''' 88 | if 'x' in code: 89 | return self.hex() 90 | if 'X' in code: 91 | return self.hex().upper() 92 | return self._bytes.__format__(code) 93 | 94 | def __repr__(self): 95 | ''' 96 | ByteData -> str 97 | ''' 98 | return '{}: {}'.format(type(self).__name__, self._bytes) 99 | 100 | def to_bytes(self) -> bytes: 101 | ''' 102 | ByteData -> bytes 103 | ''' 104 | return bytes(self._bytes) 105 | 106 | def hex(self) -> str: 107 | ''' 108 | ByteData -> hex_string 109 | ''' 110 | return self._bytes.hex() 111 | 112 | def _make_immutable(self): 113 | ''' 114 | Prevents any future changes to the object 115 | ''' 116 | self._bytes = bytes(self._bytes) 117 | self.__immutable = True 118 | 119 | def find(self, substring: Byteslike) -> int: 120 | ''' 121 | byte-like -> int 122 | Finds the index of substring 123 | ''' 124 | if isinstance(substring, ByteData): 125 | substring = substring.to_bytes() 126 | return self._bytes.find(substring) 127 | 128 | @staticmethod 129 | def validate_bytes(data: Any, length: Optional[int] = 4): 130 | ''' 131 | Raises ValueError if data is not bytes. 132 | Raises ValueError if len(data) is not length. 133 | Length may be None for unknown lengths (e.g. scripts). 134 | length=None will allow 0 length data. 135 | ''' 136 | if (not isinstance(data, ByteData) 137 | and not isinstance(data, bytes) 138 | and not isinstance(data, bytearray)): 139 | raise ValueError('Expected byte-like object. ' 140 | 'Got: {}'.format(type(data))) 141 | 142 | # allow any length 143 | if length is None: 144 | return 145 | 146 | if len(data) != length: 147 | raise ValueError('Expected byte-like object with length {}. ' 148 | 'Got {} with length {}.' 149 | .format(length, type(data), len(data))) 150 | 151 | @classmethod 152 | def from_hex(C, hex_string: str): 153 | return C.from_bytes(bytes.fromhex(hex_string)) 154 | 155 | @classmethod 156 | def from_bytes(ByteData, byte_string: bytes) -> 'ByteData': 157 | ret = ByteData() 158 | ret += byte_string 159 | return ret 160 | 161 | 162 | class VarInt(ByteData): 163 | ''' 164 | NB: number must be integer 165 | ''' 166 | def __init__(self, number: int, length: Optional[int] = None): 167 | super().__init__() 168 | if number < 0x0: 169 | raise ValueError('VarInt cannot be less than 0. ' 170 | 'Got: {}'.format(number)) 171 | if number > 0xffffffffffffffff: 172 | raise ValueError('VarInt cannot be greater than (2 ** 64) - 1. ' 173 | 'Got: {}' 174 | .format(number)) 175 | if number <= 0xfc: 176 | pass # No prefix 177 | elif number <= 0xffff or length == 3: 178 | self += bytes([0xfd]) 179 | length = 3 180 | elif number <= 0xffffffff or length == 5: 181 | self += bytes([0xfe]) 182 | length = 5 183 | elif number <= 0xffffffffffffffff or length == 9: 184 | self += bytes([0xff]) 185 | length = 9 186 | self += utils.i2le(number) 187 | 188 | if length is not None: 189 | while len(self) < length: 190 | self += b'\x00' 191 | 192 | self.number = number 193 | 194 | self._make_immutable() 195 | 196 | def copy(self) -> 'VarInt': 197 | return VarInt(self.number) 198 | 199 | @classmethod 200 | def from_bytes(VarInt, byte_string: bytes) -> 'VarInt': 201 | ''' 202 | byte-like -> VarInt 203 | accepts arbitrary length input, gets a VarInt off the front 204 | ''' 205 | num = byte_string 206 | if num[0] <= 0xfc: 207 | num = num[0:1] 208 | non_compact = False 209 | elif num[0] == 0xfd: 210 | num = num[1:3] 211 | non_compact = (num[-1:] == b'\x00') 212 | elif num[0] == 0xfe: 213 | num = num[1:5] 214 | non_compact = (num[-2:] == b'\x00\x00') 215 | elif num[0] == 0xff: 216 | num = num[1:9] 217 | non_compact = (num[-4:] == b'\x00\x00\x00\x00') 218 | if len(num) not in [1, 2, 4, 8]: 219 | raise ValueError('Malformed VarInt. Got: {}' 220 | .format(byte_string.hex())) 221 | 222 | if (non_compact 223 | and ('overwinter' in riemann.get_current_network_name() 224 | or 'sapling' in riemann.get_current_network_name())): 225 | raise ValueError('VarInt must be compact. Got: {}' 226 | .format(byte_string.hex())) 227 | 228 | ret = VarInt( 229 | utils.le2i(num), 230 | length=len(num) + 1 if non_compact else 0) 231 | 232 | return ret 233 | --------------------------------------------------------------------------------