├── 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 |
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 | [](https://travis-ci.org/summa-tx/riemann)
4 | [](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 |
42 |
43 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------