├── .github
└── workflows
│ └── python.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── decred
├── .coveragerc
├── .flake8
├── README.md
├── build.py
├── coverage-html.sh
├── debugger.sh
├── decred
│ ├── __init__.py
│ ├── btc
│ │ └── nets
│ │ │ ├── __init__.py
│ │ │ ├── mainnet.py
│ │ │ ├── regtest.py
│ │ │ ├── simnet.py
│ │ │ └── testnet.py
│ ├── crypto
│ │ ├── __init__.py
│ │ ├── crypto.py
│ │ ├── gcs.py
│ │ ├── mnemonic.py
│ │ ├── opcode.py
│ │ ├── rando.py
│ │ └── secp256k1
│ │ │ ├── __init__.py
│ │ │ ├── bytepoints.py
│ │ │ ├── curve.py
│ │ │ ├── field.pxd
│ │ │ └── field.py
│ ├── dcr
│ │ ├── __init__.py
│ │ ├── account.py
│ │ ├── addrlib.py
│ │ ├── agenda.py
│ │ ├── assets
│ │ │ ├── favicon-32x32.png
│ │ │ └── logo.svg
│ │ ├── blockchain.py
│ │ ├── constants.py
│ │ ├── dcrdata.py
│ │ ├── nets
│ │ │ ├── __init__.py
│ │ │ ├── mainnet.py
│ │ │ ├── simnet.py
│ │ │ └── testnet.py
│ │ ├── rpc.py
│ │ ├── txscript.py
│ │ ├── vsp.py
│ │ └── wire
│ │ │ ├── __init__.py
│ │ │ ├── msgblock.py
│ │ │ ├── msgping.py
│ │ │ ├── msgpong.py
│ │ │ ├── msgtx.py
│ │ │ ├── msgverack.py
│ │ │ ├── msgversion.py
│ │ │ ├── netaddress.py
│ │ │ └── wire.py
│ ├── util
│ │ ├── __init__.py
│ │ ├── chains.py
│ │ ├── database.py
│ │ ├── encode.py
│ │ ├── helpers.py
│ │ ├── tinyhttp.py
│ │ └── ws.py
│ └── wallet
│ │ ├── __init__.py
│ │ ├── accounts.py
│ │ └── wallet.py
├── examples
│ ├── create_testnet_wallet.py
│ ├── plot_ticket_price.py
│ └── send_testnet.py
├── pyproject.toml
└── tests
│ ├── benchmark
│ ├── crypto
│ │ └── secp256k1
│ │ │ └── test_field_benchmark.py
│ └── util
│ │ └── test_database_benchmark.py
│ ├── conftest.py
│ ├── integration
│ ├── dcr
│ │ ├── test_blockchain_live.py
│ │ ├── test_dcrdata_live.py
│ │ ├── test_rpc_live.py
│ │ └── test_vsp_live.py
│ ├── util
│ │ └── test_ws.py
│ └── wallet
│ │ └── test_wallet.py
│ ├── test_examples.py
│ └── unit
│ ├── btc
│ └── test_nets_btc.py
│ ├── crypto
│ ├── secp256k1
│ │ ├── test_curve.py
│ │ └── test_field.py
│ ├── test_crypto.py
│ ├── test_gcs.py
│ ├── test_mnemonic.py
│ └── test_rando.py
│ ├── dcr
│ ├── conftest.py
│ ├── test-data
│ │ ├── dcrdata.json
│ │ └── sighash.json
│ ├── test_account.py
│ ├── test_addrlib.py
│ ├── test_agenda.py
│ ├── test_blockchain_unit.py
│ ├── test_dcrdata_unit.py
│ ├── test_nets.py
│ ├── test_rpc_unit.py
│ ├── test_txscript.py
│ ├── test_vsp_unit.py
│ └── wire
│ │ ├── test_msgblock.py
│ │ ├── test_msgping.py
│ │ ├── test_msgpong.py
│ │ ├── test_msgtx.py
│ │ ├── test_msgverack.py
│ │ ├── test_msgversion.py
│ │ ├── test_netaddress.py
│ │ └── test_wire.py
│ ├── util
│ ├── test_chains.py
│ ├── test_database.py
│ ├── test_encode.py
│ ├── test_helpers.py
│ └── test_tinyhttp.py
│ └── wallet
│ └── test_accounts.py
└── tinywallet
├── .coveragerc
├── .flake8
├── README.md
├── coverage-html.sh
├── poetry.lock
├── pyproject.toml
├── tests
└── unit
│ └── test_config.py
└── tinywallet
├── __init__.py
├── app.py
├── config.py
├── fonts
├── EBGaramond-Bold.ttf
├── EBGaramond-BoldItalic.ttf
├── EBGaramond-ExtraBold.ttf
├── EBGaramond-ExtraBoldItalic.ttf
├── EBGaramond-Italic.ttf
├── EBGaramond-Medium.ttf
├── EBGaramond-MediumItalic.ttf
├── EBGaramond-Regular.ttf
├── EBGaramond-SemiBold.ttf
├── EBGaramond-SemiBoldItalic.ttf
├── MaterialIcons-Regular.ttf
├── Roboto-Black.ttf
├── Roboto-BlackItalic.ttf
├── Roboto-Bold.ttf
├── Roboto-BoldItalic.ttf
├── Roboto-Italic.ttf
├── Roboto-Light.ttf
├── Roboto-LightItalic.ttf
├── Roboto-Medium.ttf
├── Roboto-MediumItalic.ttf
├── Roboto-Regular.ttf
├── Roboto-Thin.ttf
├── Roboto-ThinItalic.ttf
├── RobotoMono-Bold.ttf
├── RobotoMono-BoldItalic.ttf
├── RobotoMono-Italic.ttf
├── RobotoMono-Light.ttf
├── RobotoMono-LightItalic.ttf
├── RobotoMono-Medium.ttf
├── RobotoMono-MediumItalic.ttf
├── RobotoMono-Regular.ttf
├── RobotoMono-Thin.ttf
└── RobotoMono-ThinItalic.ttf
├── icons
├── back.svg
├── check.svg
├── closed-eye.svg
├── folder.svg
├── gear.svg
├── home.svg
├── locked.svg
├── open-eye.svg
├── plus.svg
├── send.svg
├── spinner.svg
├── unlocked.svg
└── x.svg
├── qutilities.py
├── screens.py
└── ui.py
/.github/workflows/python.yml:
--------------------------------------------------------------------------------
1 | name: Check and test both packages
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | check-test:
6 | runs-on: ubuntu-latest
7 | strategy:
8 | matrix:
9 | python-version: [3.6, 3.7, 3.8]
10 | package: [decred, tinywallet]
11 | steps:
12 | - uses: actions/checkout@v1
13 | - name: Set up Python ${{ matrix.python-version }}
14 | uses: actions/setup-python@v1
15 | with:
16 | python-version: ${{ matrix.python-version }}
17 | - name: Install pip
18 | run: |
19 | python -m pip install --upgrade pip
20 | - name: Install the poetry tool
21 | run: |
22 | pip install poetry
23 | - name: Install the ${{ matrix.package }} package and its dependencies
24 | working-directory: ${{ format('./{0}/', matrix.package) }}
25 | run: |
26 | poetry install
27 | - name: Lint the ${{ matrix.package }} code with black
28 | working-directory: ${{ format('./{0}/', matrix.package) }}
29 | run: |
30 | # stop the build if the code was not reformatted by black
31 | poetry run black --check --diff .
32 | - name: Sort ${{ matrix.package }} import lines with isort
33 | working-directory: ${{ format('./{0}/', matrix.package) }}
34 | run: |
35 | # stop the build if import lines were not sorted by isort
36 | poetry run isort --check-only --diff --recursive .
37 | - name: Lint the ${{ matrix.package }} code with flake8
38 | working-directory: ${{ format('./{0}/', matrix.package) }}
39 | run: |
40 | # stop the build if there are syntax errors or undefined names
41 | poetry run flake8 --select=E9,F63,F7,F82 --show-source .
42 | # exit-zero treats all errors as warnings
43 | poetry run flake8 --ignore=E203,E221,E261,E731,W503 --exit-zero .
44 | - name: Test the ${{ matrix.package }} package with pytest
45 | working-directory: ${{ format('./{0}/', matrix.package) }}
46 | run: |
47 | poetry run pytest ./tests/unit/
48 | # - name: Build the ${{ matrix.package }} package
49 | # working-directory: ${{ format('./{0}/', matrix.package) }}
50 | # run: |
51 | # poetry build
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | decred/examples/wallets
3 | decred/dist/
4 | decred/poetry.lock
5 | tinywallet/dist/
6 | htmlcov/
7 | prof/
8 | .venv/
9 | .mypy_cache/
10 | __pycache__/
11 | .pytest_cache
12 | .coverage
13 | *.c
14 | *.so
15 | *.dll
16 | *.html
17 | *.egg-info
18 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution guidelines
2 |
3 | Thank you for contributing to this project! The developers are part of the
4 | [Decred community](https://decred.org/community/) and coordinate in a
5 | [chat](https://chat.decred.org/#/room/#tinydecred:decred.org) on the
6 | [Matrix](https://matrix.org/) platform, you'll need a (free) account to access
7 | it.
8 |
9 | ## Bugs and new features
10 |
11 | If you found a bug, before creating an issue please search among the
12 | [open ones](https://github.com/decred/tinydecred/issues). Please add as many
13 | useful details as you can.
14 |
15 | If you'd like to request a new feature, either create an issue (again, after
16 | searching first) or chat with us on Matrix (see above).
17 |
18 | ## Development
19 |
20 | The [Poetry](https://python-poetry.org/) tool is used for dependency management
21 | and packaging. You'll reformat your changes with the
22 | [Black](https://black.readthedocs.io/) tool and run tests using
23 | [pytest](https://www.pytest.org/).
24 |
25 | Before each pull request is merged, a Github workflow action is run to make
26 | sure that the changes meet some minimum requirements. The action definition
27 | [file](./.github/workflows/python.yml) is a useful summary of the commands
28 | you'll run while developing.
29 |
30 | Tests are written in the [pytest](https://docs.pytest.org/) format, and may be
31 | debugged more conveniently using the
32 | [PuDB](https://documen.tician.de/pudb/) console-based visual debugger, invoked
33 | via the `./decred/debugger.sh` script.
34 |
35 | For displaying line-by-line test coverage in a web browser see the
36 | `./decred/coverage-html.sh` script.
37 |
38 | ## More information
39 |
40 | Please find more information in the dcrd
41 | [contribution guidelines](https://github.com/decred/dcrd/blob/master/docs/code_contribution_guidelines.md).
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2019, Brian Stafford
4 | Copyright (c) 2019, The Decred developers
5 |
6 | Permission to use, copy, modify, and/or distribute this software for any
7 | purpose with or without fee is hereby granted, provided that the above
8 | copyright notice and this permission notice appear in all copies.
9 |
10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tinydecred
2 |
3 | TinyDecred is a Python 3 toolkit that can be used to integrate
4 | [Decred](https://decred.org/) into Python projects.
5 |
6 | The [`decred`](./decred) package contains everything needed to create wallets
7 | to send and receive DCR.
8 |
9 | The [`tinywallet`](./tinywallet) package contains a wallet based on the
10 | `decred` toolkit.
11 |
12 | Each package may be installed from the [Python Package Index](https://pypi.org/)
13 | using the [`pip`](https://pip.pypa.io/) command as usual.
14 |
15 | ## Status
16 |
17 | [](https://github.com/decred/tinydecred/actions)
18 | [](./decred/coverage-html.sh)
19 |
20 | ### PyPI
21 |
22 | [](https://pypi.org/project/decred/)
23 | [](https://docs.python.org/3/)
24 |
25 | ### GitHub
26 |
27 | [](https://github.com/decred/tinydecred/graphs/commit-activity)
28 | [](https://github.com/decred/tinydecred/graphs/contributors)
29 | [](./LICENSE)
30 |
31 | ## Roadmap
32 |
33 | In no particular order:
34 |
35 | - Staking
36 | - Schnorr signatures and Edwards curve
37 | - SPV Node
38 | - Bitcoin accounts
39 | - Decred DEX integration
40 | - Lightning network
41 |
42 | ## Contributing
43 |
44 | See the [contribution guidelines](./CONTRIBUTING.md).
45 |
--------------------------------------------------------------------------------
/decred/.coveragerc:
--------------------------------------------------------------------------------
1 | # Test coverage tool configuration, see reference at
2 | # https://coverage.readthedocs.io/en/latest/config.html
3 |
4 | [run]
5 | omit = tests/*
6 | dynamic_context = test_function
7 |
8 | [report]
9 | # Regexes for lines to exclude from consideration
10 | exclude_lines =
11 | nocover
12 |
13 | # Don't complain about debug-only code.
14 | def __repr__
15 |
16 | # Don't complain if non-runnable code isn't run.
17 | if __name__ == .__main__.:
18 |
19 | # Don't complain if tests don't hit defensive assertion code.
20 | raise AssertionError
21 | raise NotImplementedError
22 | raise CrazyKeyError
23 | raise ParameterRangeError
24 |
25 | ignore_errors = True
26 | # skip_covered = True
27 | skip_empty = True
28 |
29 | [html]
30 | show_contexts = True
31 |
--------------------------------------------------------------------------------
/decred/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | count = True
3 | max-complexity = 10
4 | max-line-length = 88
5 | statistics = True
6 | exclude =
7 | .git,
8 | .venv,
9 | .pytest_cache,
10 | __pycache__
11 |
--------------------------------------------------------------------------------
/decred/build.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details
4 |
5 | This file implements Cython integration (see https://cython.org/) to generate
6 | a C extension that speeds up the low-level secp256k1 crypto code. This is used
7 | by the Poetry tool when generating the wheel archive via its `build` command.
8 |
9 | It uses a currently undocumented Poetry feature, see:
10 | https://github.com/python-poetry/poetry/issues/11#issuecomment-379484540
11 |
12 | If Cython or a C compiler cannot be found, we skip the compilation
13 | of the C extension, and the Python code will be used.
14 |
15 | The shared library can also be built manually using the command:
16 |
17 | $ cythonize -X language_level=3 -a -i ./decred/crypto/secp256k1/field.py
18 | """
19 |
20 | from distutils.command.build_ext import build_ext
21 |
22 |
23 | class BuildExt(build_ext):
24 | def build_extensions(self):
25 | try:
26 | super().build_extensions()
27 | except Exception:
28 | pass
29 |
30 |
31 | def build(setup_kwargs):
32 | try:
33 | from Cython.Build import cythonize
34 |
35 | setup_kwargs.update(
36 | dict(
37 | ext_modules=cythonize(["decred/crypto/secp256k1/field.py"]),
38 | cmdclass=dict(build_ext=BuildExt),
39 | )
40 | )
41 | except Exception:
42 | pass
43 |
--------------------------------------------------------------------------------
/decred/coverage-html.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ## To generate line-by-line test coverage viewable in a web browser,
3 | ## change to this directory, then install the dependencies:
4 | # poetry install
5 | ## The coverage report will be in the ./htmlcov/ directory.
6 | poetry run pytest --cov-report=html --cov=decred ./tests/
7 |
--------------------------------------------------------------------------------
/decred/debugger.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Debug tests using the interactive pudb debugger.
3 | poetry run pytest --pdbcls=pudb.debugger:Debugger --trace "$1"
4 |
--------------------------------------------------------------------------------
/decred/decred/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 |
7 | class DecredError(Exception):
8 | pass
9 |
--------------------------------------------------------------------------------
/decred/decred/btc/nets/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 | """
5 |
6 | from decred import DecredError
7 |
8 | from . import mainnet, regtest, simnet, testnet
9 |
10 |
11 | the_nets = {n.Name: n for n in (mainnet, testnet, simnet, regtest)}
12 | if "testnet3" in the_nets:
13 | the_nets["testnet"] = the_nets["testnet3"]
14 | if "regtest" in the_nets:
15 | the_nets["regnet"] = the_nets["regtest"]
16 |
17 |
18 | RPCPorts = {
19 | mainnet.Name: "8332",
20 | testnet.Name: "18332",
21 | simnet.Name: "18554",
22 | regtest.Name: "18443",
23 | }
24 |
25 |
26 | def parse(name):
27 | """
28 | Get the network parameters based on the network name.
29 | """
30 | try:
31 | return the_nets[name]
32 | except KeyError:
33 | raise DecredError(f"unrecognized network name {name}")
34 |
35 |
36 | def normalizeName(netName):
37 | """
38 | Remove the numerals from testnet.
39 |
40 | Args:
41 | netName (string): The raw network name.
42 |
43 | Returns:
44 | string: The network name with numerals stripped.
45 | """
46 | return "testnet" if "testnet" in netName else netName
47 |
--------------------------------------------------------------------------------
/decred/decred/btc/nets/mainnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | mainnet holds mainnet parameters. Any values should mirror exactly
6 | https://github.com/btcsuite/btcd/blob/master/chaincfg/params.go
7 | """
8 |
9 | Name = "mainnet"
10 | DefaultPort = "8333"
11 | DNSSeeds = [
12 | ("seed.bitcoin.sipa.be", True),
13 | ("dnsseed.bluematt.me", True),
14 | ("dnsseed.bitcoin.dashjr.org", False),
15 | ("seed.bitcoinstats.com", True),
16 | ("seed.bitnodes.io", False),
17 | ("seed.bitcoin.jonasschnelli.ch", True),
18 | ]
19 |
20 | # Chain parameters
21 | GenesisHash = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
22 | PowLimit = 2 ^ 224 - 1
23 | PowLimitBits = 0x1D00FFFF
24 | BIP0034Height = (
25 | 227931 # 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
26 | )
27 | BIP0065Height = (
28 | 388381 # 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
29 | )
30 | BIP0066Height = (
31 | 363725 # 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
32 | )
33 | CoinbaseMaturity = 100
34 | SubsidyReductionInterval = 210000
35 | TargetTimespan = 60 * 60 * 24 * 14 # 14 days
36 | TargetTimePerBlock = 60 * 10 # 10 minutes
37 | RetargetAdjustmentFactor = 4 # 25% less, 400% more
38 | ReduceMinDifficulty = False
39 | MinDiffReductionTime = 0
40 | GenerateSupported = False
41 |
42 | # Checkpoints ordered from oldest to newest.
43 | Checkpoints = [
44 | (11111, "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"),
45 | (33333, "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"),
46 | (74000, "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"),
47 | (105000, "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"),
48 | (134444, "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"),
49 | (168000, "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"),
50 | (193000, "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"),
51 | (210000, "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"),
52 | (216116, "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"),
53 | (225430, "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"),
54 | (250000, "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"),
55 | (267300, "000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac"),
56 | (279000, "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"),
57 | (300255, "0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2"),
58 | (319400, "000000000000000021c6052e9becade189495d1c539aa37c58917305fd15f13b"),
59 | (343185, "0000000000000000072b8bf361d01a6ba7d445dd024203fafc78768ed4368554"),
60 | (352940, "000000000000000010755df42dba556bb72be6a32f3ce0b6941ce4430152c9ff"),
61 | (382320, "00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2"),
62 | (400000, "000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f"),
63 | (430000, "000000000000000001868b2bb3a285f3cc6b33ea234eb70facf4dcdf22186b87"),
64 | (460000, "000000000000000000ef751bbce8e744ad303c47ece06c8d863e4d417efc258c"),
65 | (490000, "000000000000000000de069137b17b8d5a3dfbd5b145b2dcfb203f15d0c4de90"),
66 | (520000, "0000000000000000000d26984c0229c9f6962dc74db0a6d525f2f1640396f69c"),
67 | (550000, "000000000000000000223b7a2298fb1c6c75fb0efc28a4c56853ff4112ec6bc9"),
68 | (560000, "0000000000000000002c7b276daf6efb2b6aa68e2ce3be67ef925b3264ae7122"),
69 | ]
70 |
71 | # Consensus rule change deployments.
72 | #
73 | # The miner confirmation window is defined as:
74 | # target proof of work timespan / target proof of work spacing
75 | RuleChangeActivationThreshold = 1916 # 95% of MinerConfirmationWindow
76 | MinerConfirmationWindow = 2016
77 |
78 | # Mempool parameters
79 | RelayNonStdTxs = False
80 |
81 | # Human-readable part for Bech32 encoded segwit addresses, as defined in
82 | # BIP 173.
83 | Bech32HRPSegwit = "bc" # always bc for main net
84 |
85 | # Address encoding magics
86 | PubKeyHashAddrID = 0x00 # starts with 1
87 | ScriptHashAddrID = 0x05 # starts with 3
88 | PrivateKeyID = 0x80 # starts with 5 (uncompressed) or K (compressed)
89 | WitnessPubKeyHashAddrID = 0x06 # starts with p2
90 | WitnessScriptHashAddrID = 0x0A # starts with 7Xh
91 |
92 | # BIP32 hierarchical deterministic extended key magics
93 | HDPrivateKeyID = (0x0488ADE4).to_bytes(4, byteorder="big") # starts with xprv
94 | HDPublicKeyID = (0x0488B21E).to_bytes(4, byteorder="big") # starts with xpub
95 |
96 | # BIP44 coin type used in the hierarchical deterministic path for
97 | # address generation.
98 | HDCoinType = 0
99 |
--------------------------------------------------------------------------------
/decred/decred/btc/nets/regtest.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | regtest holds regtest parameters. Any values should mirror exactly
6 | https://github.com/btcsuite/btcd/blob/master/chaincfg/params.go
7 | """
8 |
9 | Name = "regtest"
10 | DefaultPort = "18444"
11 | DNSSeeds = []
12 |
13 | # Chain parameters
14 | GenesisHash = "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
15 | PowLimit = 2 ^ 255 - 1
16 | PowLimitBits = 0x207FFFFF
17 | CoinbaseMaturity = 100
18 | BIP0034Height = 100000000 # Not active - Permit ver 1 blocks
19 | BIP0065Height = 1351 # Used by regression tests
20 | BIP0066Height = 1251 # Used by regression tests
21 | SubsidyReductionInterval = 150 # TargetTimespan: 14 days
22 | TargetTimePerBlock = 60 * 10 # 10 minutes
23 | RetargetAdjustmentFactor = 4 # 25% less, 400% more
24 | ReduceMinDifficulty = True
25 | MinDiffReductionTime = 60 * 20 # TargetTimePerBlock * 2
26 | GenerateSupported = True
27 |
28 | # Checkpoints ordered from oldest to newest.
29 | Checkpoints = []
30 |
31 | # Consensus rule change deployments.
32 | #
33 | # The miner confirmation window is defined as:
34 | # target proof of work timespan / target proof of work spacing
35 | RuleChangeActivationThreshold = 108 # 75% of MinerConfirmationWindow
36 | MinerConfirmationWindow = 144
37 |
38 | # Mempool parameters
39 | RelayNonStdTxs = True
40 |
41 | # Human-readable part for Bech32 encoded segwit addresses, as defined in
42 | # BIP 173.
43 | Bech32HRPSegwit = "bcrt" # always bcrt for reg test net
44 |
45 | # Address encoding magics
46 | PubKeyHashAddrID = 0x6F # starts with m or n
47 | ScriptHashAddrID = 0xC4 # starts with 2
48 | PrivateKeyID = 0xEF # starts with 9 (uncompressed) or c (compressed)
49 |
50 | # BIP32 hierarchical deterministic extended key magics
51 | HDPrivateKeyID = (0x04358394).to_bytes(4, byteorder="big") # starts with tprv
52 | HDPublicKeyID = (0x043587CF).to_bytes(4, byteorder="big") # starts with tpub
53 |
54 | # BIP44 coin type used in the hierarchical deterministic path for
55 | # address generation.
56 | HDCoinType = 1
57 |
--------------------------------------------------------------------------------
/decred/decred/btc/nets/simnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | simnet holds simnet parameters. Any values should mirror exactly
6 | https://github.com/btcsuite/btcd/blob/master/chaincfg/params.go
7 | """
8 |
9 | Name = "simnet"
10 | DefaultPort = "18555"
11 | DNSSeeds = [] # NOTE: There must NOT be any seeds.
12 |
13 | # Chain parameters
14 | GenesisHash = "683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6"
15 | PowLimit = 2 ^ 255 - 1
16 | PowLimitBits = 0x207FFFFF
17 | BIP0034Height = 0 # Always active on simnet
18 | BIP0065Height = 0 # Always active on simnet
19 | BIP0066Height = 0 # Always active on simnet
20 | CoinbaseMaturity = 100
21 | SubsidyReductionInterval = 210000
22 | TargetTimespan = 60 * 60 * 24 * 14 # 14 days
23 | TargetTimePerBlock = 60 * 10 # 10 minutes
24 | RetargetAdjustmentFactor = 4 # 25% less, 400% more
25 | ReduceMinDifficulty = True
26 | MinDiffReductionTime = 60 * 20 # TargetTimePerBlock * 2
27 | GenerateSupported = True
28 |
29 | # Checkpoints ordered from oldest to newest.
30 | Checkpoints = []
31 |
32 | # Consensus rule change deployments.
33 | #
34 | # The miner confirmation window is defined as:
35 | # target proof of work timespan / target proof of work spacing
36 | RuleChangeActivationThreshold = 75 # 75% of MinerConfirmationWindow
37 | MinerConfirmationWindow = 100
38 |
39 | # Mempool parameters
40 | RelayNonStdTxs = True
41 |
42 | # Human-readable part for Bech32 encoded segwit addresses, as defined in
43 | # BIP 173.
44 | Bech32HRPSegwit = "sb" # always sb for sim net
45 |
46 | # Address encoding magics
47 | PubKeyHashAddrID = 0x3F # starts with S
48 | ScriptHashAddrID = 0x7B # starts with s
49 | PrivateKeyID = 0x64 # starts with 4 (uncompressed) or F (compressed)
50 | WitnessPubKeyHashAddrID = 0x19 # starts with Gg
51 | WitnessScriptHashAddrID = 0x28 # starts with ?
52 |
53 | # BIP32 hierarchical deterministic extended key magics
54 | HDPrivateKeyID = (0x0420B900).to_bytes(4, byteorder="big") # starts with sprv
55 | HDPublicKeyID = (0x0420BD3A).to_bytes(4, byteorder="big") # starts with spub
56 |
57 | # BIP44 coin type used in the hierarchical deterministic path for
58 | # address generation.
59 | HDCoinType = 115 # ASCII for s
60 |
--------------------------------------------------------------------------------
/decred/decred/btc/nets/testnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | testnet holds testnet parameters. Any values should mirror exactly
6 | https://github.com/btcsuite/btcd/blob/master/chaincfg/params.go
7 | """
8 |
9 | Name = "testnet3"
10 | DefaultPort = "18333"
11 | DNSSeeds = [
12 | ("testnet-seed.bitcoin.jonasschnelli.ch", True),
13 | ("testnet-seed.bitcoin.schildbach.de", False),
14 | ("seed.tbtc.petertodd.org", True),
15 | ("testnet-seed.bluematt.me", False),
16 | ]
17 |
18 | # Chain parameters
19 | GenesisHash = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
20 | PowLimit = 2 ^ 224 - 1
21 | PowLimitBits = 0x1D00FFFF
22 | BIP0034Height = (
23 | 21111 # 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
24 | )
25 | BIP0065Height = (
26 | 581885 # 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
27 | )
28 | BIP0066Height = (
29 | 330776 # 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
30 | )
31 | CoinbaseMaturity = 100
32 | SubsidyReductionInterval = 210000
33 | TargetTimespan = 60 * 60 * 24 * 14 # 14 days
34 | TargetTimePerBlock = 60 * 10 # 10 minutes
35 | RetargetAdjustmentFactor = 4 # 25% less, 400% more
36 | ReduceMinDifficulty = True
37 | MinDiffReductionTime = 60 * 20 # TargetTimePerBlock * 2
38 | GenerateSupported = False
39 |
40 | # Checkpoints ordered from oldest to newest.
41 | Checkpoints = [
42 | (546, "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70"),
43 | (100000, "00000000009e2958c15ff9290d571bf9459e93b19765c6801ddeccadbb160a1e"),
44 | (200000, "0000000000287bffd321963ef05feab753ebe274e1d78b2fd4e2bfe9ad3aa6f2"),
45 | (300001, "0000000000004829474748f3d1bc8fcf893c88be255e6d7f571c548aff57abf4"),
46 | (400002, "0000000005e2c73b8ecb82ae2dbc2e8274614ebad7172b53528aba7501f5a089"),
47 | (500011, "00000000000929f63977fbac92ff570a9bd9e7715401ee96f2848f7b07750b02"),
48 | (600002, "000000000001f471389afd6ee94dcace5ccc44adc18e8bff402443f034b07240"),
49 | (700000, "000000000000406178b12a4dea3b27e13b3c4fe4510994fd667d7c1e6a3f4dc1"),
50 | (800010, "000000000017ed35296433190b6829db01e657d80631d43f5983fa403bfdb4c1"),
51 | (900000, "0000000000356f8d8924556e765b7a94aaebc6b5c8685dcfa2b1ee8b41acd89b"),
52 | (1000007, "00000000001ccb893d8a1f25b70ad173ce955e5f50124261bbbc50379a612ddf"),
53 | (1100007, "00000000000abc7b2cd18768ab3dee20857326a818d1946ed6796f42d66dd1e8"),
54 | (1200007, "00000000000004f2dc41845771909db57e04191714ed8c963f7e56713a7b6cea"),
55 | (1300007, "0000000072eab69d54df75107c052b26b0395b44f77578184293bf1bb1dbd9fa"),
56 | ]
57 |
58 | # Consensus rule change deployments.
59 | #
60 | # The miner confirmation window is defined as:
61 | # target proof of work timespan / target proof of work spacing
62 | RuleChangeActivationThreshold = 1512 # 75% of MinerConfirmationWindow
63 | MinerConfirmationWindow = 2016
64 |
65 | # Mempool parameters
66 | RelayNonStdTxs = True
67 |
68 | # Human-readable part for Bech32 encoded segwit addresses, as defined in
69 | # BIP 173.
70 | Bech32HRPSegwit = "tb" # always tb for test net
71 |
72 | # Address encoding magics
73 | PubKeyHashAddrID = 0x6F # starts with m or n
74 | ScriptHashAddrID = 0xC4 # starts with 2
75 | WitnessPubKeyHashAddrID = 0x03 # starts with QW
76 | WitnessScriptHashAddrID = 0x28 # starts with T7n
77 | PrivateKeyID = 0xEF # starts with 9 (uncompressed) or c (compressed)
78 |
79 | # BIP32 hierarchical deterministic extended key magics
80 | HDPrivateKeyID = (0x04358394).to_bytes(4, byteorder="big") # starts with tprv
81 | HDPublicKeyID = (0x043587CF).to_bytes(4, byteorder="big") # starts with tpub
82 |
83 | # BIP44 coin type used in the hierarchical deterministic path for
84 | # address generation.
85 | HDCoinType = 1
86 |
--------------------------------------------------------------------------------
/decred/decred/crypto/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/decred/decred/crypto/__init__.py
--------------------------------------------------------------------------------
/decred/decred/crypto/rando.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | Copyright (c) 2019-20, The Decred developers
4 | See LICENSE for details
5 | """
6 |
7 | import os
8 |
9 | from decred import DecredError
10 | from decred.util.encode import ByteArray
11 |
12 |
13 | KEY_SIZE = 32
14 | HASH_SIZE = 32
15 |
16 | MinSeedBytes = 16 # 128 bits
17 | MaxSeedBytes = 64 # 512 bits
18 |
19 |
20 | def checkSeedLength(length):
21 | """
22 | Check that seed length is correct.
23 |
24 | Args:
25 | length int: the seed length to be checked.
26 |
27 | Raises:
28 | DecredError if length is not between MinSeedBytes and MaxSeedBytes
29 | included.
30 | """
31 | if length < MinSeedBytes or length > MaxSeedBytes:
32 | raise DecredError(f"Invalid seed length {length}")
33 |
34 |
35 | def generateSeed(length=MaxSeedBytes):
36 | """
37 | Generate a cryptographically-strong random seed.
38 |
39 | Returns:
40 | bytes: a random bytes object of the given length.
41 |
42 | Raises:
43 | DecredError if length is not between MinSeedBytes and MaxSeedBytes
44 | included.
45 | """
46 | checkSeedLength(length)
47 | return os.urandom(length)
48 |
49 |
50 | def newHashRaw():
51 | """
52 | Generate a random hash of HASH_SIZE length.
53 |
54 | Returns:
55 | bytes: a random object of HASH_SIZE length.
56 | """
57 | return generateSeed(HASH_SIZE)
58 |
59 |
60 | def newHash():
61 | """
62 | Generate a wrapped random hash of HASH_SIZE length.
63 |
64 | Returns:
65 | ByteArray: a random object of HASH_SIZE length.
66 | """
67 | return ByteArray(newHashRaw())
68 |
69 |
70 | def newKeyRaw():
71 | """
72 | Generate a random key of KEY_SIZE length.
73 |
74 | Returns:
75 | bytes: a random object of KEY_SIZE length.
76 | """
77 | return generateSeed(KEY_SIZE)
78 |
79 |
80 | def newKey():
81 | """
82 | Generate a wrapped random key of KEY_SIZE length.
83 |
84 | Returns:
85 | ByteArray: a random object of KEY_SIZE length.
86 | """
87 | return ByteArray(newKeyRaw())
88 |
--------------------------------------------------------------------------------
/decred/decred/crypto/secp256k1/__init__.py:
--------------------------------------------------------------------------------
1 | __all__ = ["curve"]
2 |
--------------------------------------------------------------------------------
/decred/decred/crypto/secp256k1/field.pxd:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details
4 |
5 | Definitions allowing Cython to generate optimized C code that builds a
6 | dynamic library speeding up the field.py code.
7 | """
8 |
9 | import cython
10 |
11 |
12 | cdef unsigned long twoBitsMask = 0x03
13 | cdef unsigned long fourBitsMask = 0x0F
14 | cdef unsigned long sixBitsMask = 0x3F
15 | cdef unsigned long eightBitsMask = 0xFF
16 | cdef unsigned long fieldWords = 10
17 | cdef unsigned long fieldBase = 26
18 | cdef unsigned long fieldBaseMask = (1 << fieldBase) - 1
19 | cdef unsigned long fieldMSBBits = 256 - (fieldBase * (fieldWords - 1))
20 | cdef unsigned long fieldMSBMask = (1 << fieldMSBBits) - 1
21 | cdef unsigned long fieldPrimeWordZero = 0x3FFFC2F
22 | cdef unsigned long fieldPrimeWordOne = 0x3FFFFBF
23 | cdef unsigned long primePartBy16 = 68719492368
24 |
25 |
26 | cdef class FieldVal:
27 | cdef public unsigned long n[10]
28 |
29 | @cython.locals(
30 | m=cython.ulong,
31 | t0=cython.ulong,
32 | t1=cython.ulong,
33 | t2=cython.ulong,
34 | t3=cython.ulong,
35 | t4=cython.ulong,
36 | t5=cython.ulong,
37 | t6=cython.ulong,
38 | t7=cython.ulong,
39 | t8=cython.ulong,
40 | t9=cython.ulong,
41 | )
42 | cpdef normalize(self)
43 |
44 | cpdef negateVal(self, FieldVal val, unsigned long magnitude)
45 |
46 | cpdef add(self, FieldVal val)
47 |
48 | @cython.locals(
49 | m=cython.ulong,
50 | n=cython.ulong,
51 | t0=cython.ulong,
52 | t1=cython.ulong,
53 | t2=cython.ulong,
54 | t3=cython.ulong,
55 | t4=cython.ulong,
56 | t5=cython.ulong,
57 | t6=cython.ulong,
58 | t7=cython.ulong,
59 | t8=cython.ulong,
60 | t9=cython.ulong,
61 | t10=cython.ulong,
62 | t11=cython.ulong,
63 | t12=cython.ulong,
64 | t13=cython.ulong,
65 | t14=cython.ulong,
66 | t15=cython.ulong,
67 | t16=cython.ulong,
68 | t17=cython.ulong,
69 | t18=cython.ulong,
70 | t19=cython.ulong,
71 | )
72 | cpdef squareVal(self, FieldVal val)
73 |
74 | cpdef mulInt(self, long val)
75 |
76 | @cython.locals(
77 | d=cython.ulong,
78 | m=cython.ulong,
79 | t0=cython.ulong,
80 | t1=cython.ulong,
81 | t2=cython.ulong,
82 | t3=cython.ulong,
83 | t4=cython.ulong,
84 | t5=cython.ulong,
85 | t6=cython.ulong,
86 | t7=cython.ulong,
87 | t8=cython.ulong,
88 | t9=cython.ulong,
89 | t10=cython.ulong,
90 | t11=cython.ulong,
91 | t12=cython.ulong,
92 | t13=cython.ulong,
93 | t14=cython.ulong,
94 | t15=cython.ulong,
95 | t16=cython.ulong,
96 | t17=cython.ulong,
97 | t18=cython.ulong,
98 | t19=cython.ulong,
99 | )
100 | cpdef mul2(self, FieldVal val, FieldVal val2)
101 |
102 | cpdef add2(self, FieldVal val, FieldVal val2)
103 |
104 | cpdef putBytes(self, char b[32])
105 |
106 | # inverse relies heavily on FieldVal methods, preventing optimization.
107 | # @cython.locals(
108 | # a2=FieldVal,
109 | # a3=FieldVal,
110 | # a4=FieldVal,
111 | # a10=FieldVal,
112 | # a11=FieldVal,
113 | # a21=FieldVal,
114 | # a42=FieldVal,
115 | # a45=FieldVal,
116 | # a63=FieldVal,
117 | # a1019=FieldVal,
118 | # a1023=FieldVal,
119 | # )
120 | # cpdef inverse(self)
121 |
--------------------------------------------------------------------------------
/decred/decred/dcr/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/decred/decred/dcr/__init__.py
--------------------------------------------------------------------------------
/decred/decred/dcr/agenda.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, Brian Stafford
3 | Copyright (c) 2020, the Decred developers
4 | See LICENSE for details
5 | """
6 |
7 |
8 | class AgendaInfo:
9 | """
10 | Models data for deployments of a GetBlockChainInfoResult.
11 | """
12 |
13 | def __init__(self, status, since, startTime, expireTime):
14 | """
15 | Args:
16 | status (str): one of "defined", "started", "lockedin", "active",
17 | "failed".
18 | since (int): height of last state change.
19 | startTime (int): start time.
20 | expireTime (int): end time.
21 | """
22 | self.status = status
23 | self.since = since
24 | self.startTime = startTime
25 | self.expireTime = expireTime
26 |
27 | @staticmethod
28 | def parse(obj):
29 | """
30 | Parse the AgendaInfo from the decoded RPC response.
31 |
32 | Args:
33 | obj (dict): the decoded dcrd RPC response.
34 |
35 | Returns:
36 | AgendaInfo: the AgendaInfo.
37 | """
38 | return AgendaInfo(
39 | status=obj["status"],
40 | since=obj.get("since", 0),
41 | startTime=obj["starttime"],
42 | expireTime=obj["expiretime"],
43 | )
44 |
45 |
46 | class AgendaChoices:
47 | """
48 | Agenda individual choices such as abstain, yes, no.
49 | """
50 |
51 | def __init__(self, id_, description, bits, isAbstain, isNo, count, progress):
52 | """
53 | Args:
54 | id_ (str): unique identifier of this choice.
55 | description (str): description of this choice.
56 | bits (int): bits that identify this choice.
57 | isAbstain (bool): this choice is to abstain from change.
58 | isNo (bool): hard no choice (1 and only 1 per agenda).
59 | count (int): how many votes received.
60 | progress (float): progress of the overall count.
61 | """
62 | self.id = id_
63 | self.description = description
64 | self.bits = bits
65 | self.isAbstain = isAbstain
66 | self.isNo = isNo
67 | self.count = count
68 | self.progress = progress
69 |
70 | def __eq__(self, other):
71 | try:
72 | return self.id == other.id
73 | except AttributeError:
74 | return False
75 |
76 | @staticmethod
77 | def parse(obj):
78 | """
79 | Parse the AgendaChoices from the decoded RPC response.
80 |
81 | Args:
82 | obj (dict): the decoded dcrd RPC response.
83 |
84 | Returns:
85 | AgendaChoices: the parsed AgendaChoices.
86 | """
87 | return AgendaChoices(
88 | id_=obj["id"],
89 | description=obj["description"],
90 | bits=obj["bits"],
91 | isAbstain=obj["isabstain"],
92 | isNo=obj["isno"],
93 | count=obj["count"],
94 | progress=obj["progress"],
95 | )
96 |
97 |
98 | class Agenda:
99 | """
100 | An agenda with name, description, and its AgendaChoices.
101 | """
102 |
103 | def __init__(
104 | self,
105 | id_,
106 | description,
107 | mask,
108 | startTime,
109 | expireTime,
110 | status,
111 | quorumProgress,
112 | choices,
113 | ):
114 | """
115 | Args:
116 | id_ (str): unique identifier of this agenda.
117 | description (str): description of this agenda.
118 | mask (int): agenda mask.
119 | startTime (int): time agenda becomes valid.
120 | expireTime (int): time agenda becomes invalid.
121 | status (str): agenda status.
122 | quorumProgress (float): progress of quorum reached.
123 | choices list(AgendaChoices): all choices in this agenda.
124 | """
125 | self.id = id_
126 | self.description = description
127 | self.mask = mask
128 | self.startTime = startTime
129 | self.expireTime = expireTime
130 | self.status = status
131 | self.quorumProgress = quorumProgress
132 | self.choices = choices
133 |
134 | def __eq__(self, other):
135 | try:
136 | return self.id == other.id
137 | except AttributeError:
138 | return False
139 |
140 | @staticmethod
141 | def parse(obj):
142 | """
143 | Parse the Agenda from the decoded RPC response.
144 |
145 | Args:
146 | obj (dict): the decoded dcrd RPC response.
147 |
148 | Returns:
149 | Agenda: the parsed Agenda info.
150 | """
151 | return Agenda(
152 | id_=obj["id"],
153 | description=obj["description"],
154 | mask=obj["mask"],
155 | startTime=obj["starttime"],
156 | expireTime=obj["expiretime"],
157 | status=obj["status"],
158 | quorumProgress=obj["quorumprogress"],
159 | choices=[AgendaChoices.parse(choice) for choice in obj["choices"]],
160 | )
161 |
162 |
163 | class AgendasInfo:
164 | """
165 | All current agenda information for the current network. agendas contains
166 | a list of Agenda.
167 | """
168 |
169 | def __init__(
170 | self,
171 | currentHeight,
172 | startHeight,
173 | endHeight,
174 | hash_,
175 | voteVersion,
176 | quorum,
177 | totalVotes,
178 | agendas,
179 | ):
180 | """
181 | Args:
182 | currentHeight (int): the current height.
183 | startHeight (int): the initial height.
184 | endHeight (int): the final height.
185 | hash_ (str): the hash.
186 | voteVersion (int): the vote version.
187 | quorum (float): the quorum.
188 | totalVotes (int): the total number of votes.
189 | agendas list(Agenda): all agendas in this AgendasInfo.
190 | """
191 | self.currentHeight = currentHeight
192 | self.startHeight = startHeight
193 | self.endHeight = endHeight
194 | self.hash = hash_
195 | self.voteVersion = voteVersion
196 | self.quorum = quorum
197 | self.totalVotes = totalVotes
198 | self.agendas = agendas
199 |
200 | @staticmethod
201 | def parse(obj):
202 | """
203 | Parse the AgendasInfo from the decoded RPC response.
204 |
205 | Args:
206 | obj (dict): the decoded dcrd RPC response.
207 |
208 | Returns:
209 | AgendasInfo: the AgendasInfo.
210 | """
211 | return AgendasInfo(
212 | currentHeight=obj["currentheight"],
213 | startHeight=obj["startheight"],
214 | endHeight=obj["endheight"],
215 | hash_=obj["hash"],
216 | voteVersion=obj["voteversion"],
217 | quorum=obj["quorum"],
218 | totalVotes=obj["totalvotes"],
219 | agendas=[Agenda.parse(agenda) for agenda in obj["agendas"]],
220 | )
221 |
--------------------------------------------------------------------------------
/decred/decred/dcr/assets/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/decred/decred/dcr/assets/favicon-32x32.png
--------------------------------------------------------------------------------
/decred/decred/dcr/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
100 |
--------------------------------------------------------------------------------
/decred/decred/dcr/constants.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | See LICENSE for details
4 |
5 | Just some constants.
6 | """
7 |
8 | import os.path
9 |
10 |
11 | ASSETS_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "assets")
12 | FAVICON = os.path.join(ASSETS_DIR, "favicon-32x32.png")
13 | LOGO = os.path.join(ASSETS_DIR, "logo.svg")
14 | INF = float("inf")
15 | MINUTE = 60
16 | DAY = 86400
17 | HOUR = 3600
18 | SYMBOL = "DCR"
19 |
--------------------------------------------------------------------------------
/decred/decred/dcr/nets/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, The Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from decred import DecredError
7 |
8 | from . import mainnet, simnet, testnet
9 |
10 |
11 | the_nets = {n.Name: n for n in (mainnet, testnet, simnet)}
12 | if "testnet3" in the_nets:
13 | the_nets["testnet"] = the_nets["testnet3"]
14 |
15 |
16 | DcrdPorts = {
17 | mainnet.Name: "9109",
18 | testnet.Name: "19109",
19 | simnet.Name: "19556",
20 | }
21 |
22 |
23 | def parse(name):
24 | """
25 | Get the network parameters based on the network name.
26 | """
27 | # Set testnet to DCR for now. If more coins are added, a better solution
28 | # will be needed.
29 | try:
30 | return the_nets[name]
31 | except KeyError:
32 | raise DecredError(f"unrecognized network name {name}")
33 |
34 |
35 | def normalizeName(netName):
36 | """
37 | Remove the numerals from testnet.
38 |
39 | Args:
40 | netName (string): The raw network name.
41 |
42 | Returns:
43 | string: The network name with numerals stripped.
44 | """
45 | return "testnet" if "testnet" in netName else netName
46 |
--------------------------------------------------------------------------------
/decred/decred/dcr/nets/mainnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | Copyright (c) 2019, The Decred developers
4 | See LICENSE for details
5 |
6 | mainnet holds mainnet parameters. Any values should mirror exactly
7 | https://github.com/decred/dcrd/blob/master/chaincfg/mainnetparams.go
8 | """
9 |
10 | from decred.dcr import constants as C
11 |
12 |
13 | Name = "mainnet"
14 | DefaultPort = "9108"
15 | DNSSeeds = [
16 | ("mainnet-seed.decred.mindcry.org", True),
17 | ("mainnet-seed.decred.netpurgatory.com", True),
18 | ("mainnet-seed.decred.org", True),
19 | ]
20 | GenesisHash = "298e5cc3d985bfe7f81dc135f360abe089edd4396b86d2de66b0cef42b21d980"
21 | PowLimit = 2 ^ 224 - 1
22 |
23 | # POW parameters
24 | PowLimitBits = 0x1D00FFFF
25 | ReduceMinDifficulty = False
26 | MinDiffReductionTime = 0 # Does not apply since ReduceMinDifficulty false
27 | GenerateSupported = False
28 | MaximumBlockSizes = [393216]
29 | MaxTxSize = 393216
30 | TargetTimePerBlock = C.MINUTE * 5
31 | WorkDiffAlpha = 1
32 | WorkDiffWindowSize = 144
33 | WorkDiffWindows = 20
34 | TargetTimespan = C.MINUTE * 5 * 144 # TimePerBlock * WindowSize
35 | RetargetAdjustmentFactor = 4
36 |
37 | # Subsidy parameters.
38 | BaseSubsidy = 3119582664 # 21m
39 | MulSubsidy = 100
40 | DivSubsidy = 101
41 | SubsidyReductionInterval = 6144
42 | WorkRewardProportion = 6
43 | StakeRewardProportion = 3
44 | BlockTaxProportion = 1
45 |
46 | # Checkpoints ordered from oldest to newest.
47 | Checkpoints = [
48 | (440, "0000000000002203eb2c95ee96906730bb56b2985e174518f90eb4db29232d93"),
49 | (24480, "0000000000000c9d4239c4ef7ef3fb5aaeed940244bc69c57c8c5e1f071b28a6"),
50 | (48590, "0000000000000d5e0de21a96d3c965f5f2db2c82612acd7389c140c9afe92ba7"),
51 | (54770, "00000000000009293d067b1126b7de07fc9b2b94ee50dfe0d48c239a7adb072c"),
52 | (60720, "0000000000000a64475d68ffb9ad89a3d147c0f5138db26b40da9d19d0004117"),
53 | (65270, "0000000000000021f107601962789b201f0a0cbb98ac5f8c12b93d94e795b441"),
54 | (75380, "0000000000000e7d13cfc85806aa720fe3670980f5b7d33253e4f41985558372"),
55 | (85410, "00000000000013ec928074bea6eac9754aa614c7acb20edf300f18b0cd122692"),
56 | (99880, "0000000000000cb2a9a9ded647b9f78aae51ace32dd8913701d420ead272913c"),
57 | (123080, "000000000000009ea6e02d0f0424f445ed50686f9ae4aecdf3b268e981114477"),
58 | (135960, "00000000000001d2f9bbca9177972c0ba45acb40836b72945a75d73b99079498"),
59 | (139740, "00000000000001397179ae1aff156fb1aea228938d06b83e43b78b1c44527b5b"),
60 | (155900, "000000000000008557e37fb05177fc5a54e693de20689753639135f85a2dcb2e"),
61 | (164300, "000000000000009ed067ff51cd5e15f3c786222a5183b20a991a80ce535907a9"),
62 | (181020, "00000000000000b77d832cb2cbed02908d69323862a53e56345400ad81a6fb8f"),
63 | (189950, "000000000000007341d8ae2ea7e41f25cee00e1a70a4a3dc1cb055d14ecb2e11"),
64 | (214672, "0000000000000021d5cbeead55cb7fd659f07e8127358929ffc34cd362209758"),
65 | (259810, "0000000000000000ee0fbf469a9f32477ffbb46ebd7a280a53c842ab4243f97c"),
66 | (295940, "0000000000000000148852c8a919addf4043f9f267b13c08df051d359f1622ca"),
67 | ]
68 |
69 | # The miner confirmation window is defined as:
70 | # target proof of work timespan / target proof of work spacing
71 | RuleChangeActivationQuorum = (
72 | 4032 # 10 % of RuleChangeActivationInterval * TicketsPerBlock
73 | )
74 | RuleChangeActivationMultiplier = 3 # 75%
75 | RuleChangeActivationDivisor = 4
76 | RuleChangeActivationInterval = 2016 * 4 # 4 weeks
77 |
78 |
79 | # Enforce current block version once majority of the network has
80 | # upgraded.
81 | # 75% (750 / 1000)
82 | # Reject previous block versions once a majority of the network has
83 | # upgraded.
84 | # 95% (950 / 1000)
85 | BlockEnforceNumRequired = 750
86 | BlockRejectNumRequired = 950
87 | BlockUpgradeNumToCheck = 1000
88 |
89 | # AcceptNonStdTxs is a mempool param to either accept and relay
90 | # non standard txs to the network or reject them
91 | AcceptNonStdTxs = False
92 |
93 | # Address encoding magics
94 | NetworkAddressPrefix = "D"
95 | PubKeyAddrID = (0x1386).to_bytes(2, byteorder="big") # starts with Dk
96 | PubKeyHashAddrID = (0x073F).to_bytes(2, byteorder="big") # starts with Ds
97 | PKHEdwardsAddrID = (0x071F).to_bytes(2, byteorder="big") # starts with De
98 | PKHSchnorrAddrID = (0x0701).to_bytes(2, byteorder="big") # starts with DS
99 | ScriptHashAddrID = (0x071A).to_bytes(2, byteorder="big") # starts with Dc
100 | PrivateKeyID = (0x22DE).to_bytes(2, byteorder="big") # starts with Pm
101 |
102 | # BIP32 hierarchical deterministic extended key magics
103 | HDPrivateKeyID = (0x02FDA4E8).to_bytes(4, byteorder="big") # starts with dprv
104 | HDPublicKeyID = (0x02FDA926).to_bytes(4, byteorder="big") # starts with dpub
105 |
106 | # BIP44 coin type used in the hierarchical deterministic path for
107 | # address generation.
108 | SLIP0044CoinType = 42 # SLIP0044, Decred
109 | LegacyCoinType = 20 # for backwards compatibility
110 |
111 | # Decred PoS parameters
112 | MinimumStakeDiff = 2 * 1e8 # 2 Coin
113 | TicketPoolSize = 8192
114 | TicketsPerBlock = 5
115 | TicketMaturity = 256
116 | TicketExpiry = 40960 # 5*TicketPoolSize
117 | CoinbaseMaturity = 256
118 | SStxChangeMaturity = 1
119 | TicketPoolSizeWeight = 4
120 | StakeDiffAlpha = 1 # Minimal
121 | StakeDiffWindowSize = 144
122 | StakeDiffWindows = 20
123 | StakeVersionInterval = 144 * 2 * 7 # ~1 week
124 | MaxFreshStakePerBlock = 20 # 4*TicketsPerBlock
125 | StakeEnabledHeight = 256 + 256 # CoinbaseMaturity + TicketMaturity
126 | StakeValidationHeight = 4096 # ~14 days
127 | StakeBaseSigScript = (0x0000).to_bytes(2, byteorder="big")
128 | StakeMajorityMultiplier = 3
129 | StakeMajorityDivisor = 4
130 |
131 | OrganizationPkScript = (0xA914F5916158E3E2C4551C1796708DB8367207ED13BB87).to_bytes(
132 | 23, byteorder="big"
133 | )
134 | OrganizationPkScriptVersion = 0
135 |
136 | # Convenience constants
137 | GENESIS_STAMP = 1454954400
138 | STAKE_SPLIT = 0.3
139 | POW_SPLIT = 0.6
140 | TREASURY_SPLIT = 0.1
141 |
142 | BlockOneSubsidy = int(1680000 * 1e8)
143 |
--------------------------------------------------------------------------------
/decred/decred/dcr/nets/simnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | Copyright (c) 2019, The Decred developers
4 | See LICENSE for details
5 |
6 | simnet holds simnet parameters. Any values should mirror exactly
7 | https://github.com/decred/dcrd/blob/master/chaincfg/simnetparams.go
8 | """
9 |
10 | # SimNetParams defines the network parameters for the simulation test network.
11 | # This network is similar to the normal test network except it is intended for
12 | # private use within a group of individuals doing simulation testing and full
13 | # integration tests between different applications such as wallets, voting
14 | # service providers, mining pools, block explorers, and other services that
15 | # build on Decred.
16 | #
17 | # The functionality is intended to differ in that the only nodes which are
18 | # specifically specified are used to create the network rather than following
19 | # normal discovery rules. This is important as otherwise it would just turn
20 | # into another public testnet.
21 | Name = "simnet"
22 | DefaultPort = "18555"
23 | DNSSeeds = None # NOTE: There must NOT be any seeds.
24 |
25 | # Chain parameters
26 | GenesisHash = "5bec7567af40504e0994db3b573c186fffcc4edefe096ff2e58d00523bd7e8a6"
27 | PowLimit = 2 ** 255 - 1
28 | PowLimitBits = 0x207FFFFF
29 | ReduceMinDifficulty = False
30 | MinDiffReductionTime = 0 # Does not apply since ReduceMinDifficulty fals
31 | GenerateSupported = False
32 | MaximumBlockSizes = [1310720]
33 | MaxTxSize = 1000000
34 | TargetTimePerBlock = 1 # one secon
35 | WorkDiffAlpha = 1
36 | WorkDiffWindowSize = 8
37 | WorkDiffWindows = 4
38 | TargetTimespan = 8 # TimePerBlock * WindowSize
39 | RetargetAdjustmentFactor = 4
40 |
41 | # Subsidy parameters.
42 | BaseSubsidy = 50000000000
43 | MulSubsidy = 100
44 | DivSubsidy = 101
45 | SubsidyReductionInterval = 128
46 | WorkRewardProportion = 6
47 | StakeRewardProportion = 3
48 | BlockTaxProportion = 1
49 |
50 | # Checkpoints ordered from oldest to newest.
51 | Checkpoints = (None,)
52 |
53 | # Consensus rule change deployments.
54 | #
55 | # The miner confirmation window is defined as:
56 | # target proof of work timespan / target proof of work spacing
57 | # 10% of RuleChangeActivationInterval * TicketsPerBlock
58 | RuleChangeActivationQuorum = 160
59 | RuleChangeActivationMultiplier = 3 # 75%
60 | RuleChangeActivationDivisor = 4
61 | RuleChangeActivationInterval = 320 # 320 seconds
62 |
63 | # Enforce current block version once majority of the network has upgraded.
64 | # 51% (51 / 100)
65 | # Reject previous block versions once a majority of the network has upgraded.
66 | # 75% (75 / 100)
67 | BlockEnforceNumRequired = 51
68 | BlockRejectNumRequired = 75
69 | BlockUpgradeNumToCheck = 100
70 |
71 | # AcceptNonStdTxs is a mempool param to either accept and relay
72 | # non standard txs to the network or reject them
73 | AcceptNonStdTxs = True
74 |
75 | # Address encoding magics
76 | NetworkAddressPrefix = ("S",)
77 | PubKeyAddrID = (0x276F).to_bytes(2, byteorder="big") # starts with Sk
78 | PubKeyHashAddrID = (0x0E91).to_bytes(2, byteorder="big") # starts with Ss
79 | PKHEdwardsAddrID = (0x0E71).to_bytes(2, byteorder="big") # starts with Se
80 | PKHSchnorrAddrID = (0x0E53).to_bytes(2, byteorder="big") # starts with SS
81 | ScriptHashAddrID = (0x0E6C).to_bytes(2, byteorder="big") # starts with Sc
82 | PrivateKeyID = (0x2307).to_bytes(2, byteorder="big") # starts with Ps
83 |
84 | # BIP32 hierarchical deterministic extended key magics
85 | HDPrivateKeyID = (0x0420B903).to_bytes(4, byteorder="big") # starts with sprv
86 | HDPublicKeyID = (0x0420BD3D).to_bytes(4, byteorder="big") # starts with spub
87 |
88 | # BIP44 coin type used in the hierarchical deterministic path for
89 | # address generation.
90 | SLIP0044CoinType = 1 # SLIP0044, Testnet (all coins)
91 | LegacyCoinType = 115 # ASCII for s, for backwards compatibility
92 |
93 | # Decred PoS parameters
94 | MinimumStakeDiff = 20000
95 | TicketPoolSize = 64
96 | TicketsPerBlock = 5
97 | TicketMaturity = 16
98 | TicketExpiry = 384 # 6*TicketPoolSize
99 | CoinbaseMaturity = 16
100 | SStxChangeMaturity = 1
101 | TicketPoolSizeWeight = 4
102 | StakeDiffAlpha = 1
103 | StakeDiffWindowSize = 8
104 | StakeDiffWindows = 8
105 | StakeVersionInterval = 8 * 2 * 7
106 | MaxFreshStakePerBlock = 20 # 4*TicketsPerBlock
107 | StakeEnabledHeight = 16 + 16 # CoinbaseMaturity + TicketMaturity
108 | StakeValidationHeight = 16 + (64 * 2) # CoinbaseMaturity + TicketPoolSize*2
109 | StakeBaseSigScript = (0xDEADBEEF).to_bytes(4, byteorder="big")
110 | StakeMajorityMultiplier = 3
111 | StakeMajorityDivisor = 4
112 |
113 | # Decred organization related parameters
114 | #
115 | # Treasury address is a 3-of-3 P2SH going to a wallet with seed:
116 | # aardvark adroitness aardvark adroitness
117 | # aardvark adroitness aardvark adroitness
118 | # aardvark adroitness aardvark adroitness
119 | # aardvark adroitness aardvark adroitness
120 | # aardvark adroitness aardvark adroitness
121 | # aardvark adroitness aardvark adroitness
122 | # aardvark adroitness aardvark adroitness
123 | # aardvark adroitness aardvark adroitness
124 | # briefcase
125 | # (seed 0x0000000000000000000000000000000000000000000000000000000000000000)
126 | #
127 | # This same wallet owns the three ledger outputs for simnet.
128 | #
129 | # P2SH details for simnet treasury:
130 | #
131 | # redeemScript: 532103e8c60c7336744c8dcc7b85c27789950fc52aa4e48f895ebbfb
132 | # ac383ab893fc4c2103ff9afc246e0921e37d12e17d8296ca06a8f92a07fbe7857ed1d4
133 | # f0f5d94e988f21033ed09c7fa8b83ed53e6f2c57c5fa99ed2230c0d38edf53c0340d0f
134 | # c2e79c725a53ae
135 | # (3-of-3 multisig)
136 | # Pubkeys used:
137 | # SkQmxbeuEFDByPoTj41TtXat8tWySVuYUQpd4fuNNyUx51tF1csSs
138 | # SkQn8ervNvAUEX5Ua3Lwjc6BAuTXRznDoDzsyxgjYqX58znY7w9e4
139 | # SkQkfkHZeBbMW8129tZ3KspEh1XBFC1btbkgzs6cjSyPbrgxzsKqk
140 | #
141 | # Organization address is ScuQxvveKGfpG1ypt6u27F99Anf7EW3cqhq
142 | OrganizationPkScript = (0xA914CBB08D6CA783B533B2C7D24A51FBCA92D937BF9987).to_bytes(
143 | 23, byteorder="big"
144 | )
145 | OrganizationPkScriptVersion = 0
146 | # BlockOneLedger = BlockOneLedgerSimNet,
147 | BlockOneSubsidy = int(300000 * 1e8)
148 |
--------------------------------------------------------------------------------
/decred/decred/dcr/nets/testnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | Copyright (c) 2019, The Decred developers
4 | See LICENSE for details
5 |
6 | testnet holds testnet3 parameters. Any values should mirror exactly
7 | https://github.com/decred/dcrd/blob/master/chaincfg/testnetparams.go
8 | """
9 |
10 | Name = "testnet3"
11 | DefaultPort = "19108"
12 | DNSSeeds = [
13 | ("testnet-seed.decred.mindcry.org", True),
14 | ("testnet-seed.decred.netpurgatory.com", True),
15 | ("testnet-seed.decred.org", True),
16 | ]
17 |
18 | GenesisHash = "a649dce53918caf422e9c711c858837e08d626ecfcd198969b24f7b634a49bac"
19 | PowLimit = 2 ^ 232 - 1
20 | PowLimitBits = 0x1E00FFFF
21 | ReduceMinDifficulty = True
22 | MinDiffReductionTime = 60 * 10 # ~99.3% chance to be mined before reduction
23 | GenerateSupported = True
24 | MaximumBlockSizes = [1310720]
25 | MaxTxSize = 1000000
26 | TargetTimePerBlock = 60 * 2
27 | WorkDiffAlpha = 1
28 | WorkDiffWindowSize = 144
29 | WorkDiffWindows = 20
30 | TargetTimespan = 60 * 2 * 144 # TimePerBlock * WindowSize
31 | RetargetAdjustmentFactor = 4
32 |
33 | # Subsidy parameters.
34 | BaseSubsidy = 2500000000 # 25 Coin
35 | MulSubsidy = 100
36 | DivSubsidy = 101
37 | SubsidyReductionInterval = 2048
38 | WorkRewardProportion = 6
39 | StakeRewardProportion = 3
40 | BlockTaxProportion = 1
41 |
42 | # Consensus rule change deployments.
43 | #
44 | # The miner confirmation window is defined as:
45 | # target proof of work timespan / target proof of work spacing
46 | RuleChangeActivationQuorum = (
47 | 2520 # 10 % of RuleChangeActivationInterval * TicketsPerBlock
48 | )
49 | RuleChangeActivationMultiplier = 3 # 75%
50 | RuleChangeActivationDivisor = 4
51 | RuleChangeActivationInterval = 5040 # 1 week
52 |
53 | # Enforce current block version once majority of the network has
54 | # upgraded.
55 | # 51% (51 / 100)
56 | # Reject previous block versions once a majority of the network has
57 | # upgraded.
58 | # 75% (75 / 100)
59 | BlockEnforceNumRequired = 51
60 | BlockRejectNumRequired = 75
61 | BlockUpgradeNumToCheck = 100
62 |
63 | # AcceptNonStdTxs is a mempool param to either accept and relay
64 | # non standard txs to the network or reject them
65 | AcceptNonStdTxs = True
66 |
67 | # Address encoding magics
68 | NetworkAddressPrefix = "T"
69 | PubKeyAddrID = (0x28F7).to_bytes(2, byteorder="big") # starts with Tk
70 | PubKeyHashAddrID = (0x0F21).to_bytes(2, byteorder="big") # starts with Ts
71 | PKHEdwardsAddrID = (0x0F01).to_bytes(2, byteorder="big") # starts with Te
72 | PKHSchnorrAddrID = (0x0EE3).to_bytes(2, byteorder="big") # starts with TS
73 | ScriptHashAddrID = (0x0EFC).to_bytes(2, byteorder="big") # starts with Tc
74 | PrivateKeyID = (0x230E).to_bytes(2, byteorder="big") # starts with Pt
75 |
76 | # BIP32 hierarchical deterministic extended key magics
77 | HDPrivateKeyID = (0x04358397).to_bytes(4, byteorder="big") # starts with tprv
78 | HDPublicKeyID = (0x043587D1).to_bytes(4, byteorder="big") # starts with tpub
79 |
80 | # BIP44 coin type used in the hierarchical deterministic path for
81 | # address generation.
82 | SLIP0044CoinType = 1 # SLIP0044, Testnet (all coins)
83 | LegacyCoinType = 11 # for backwards compatibility
84 |
85 | # Decred PoS parameters
86 | MinimumStakeDiff = 20000000 # 0.2 Coin
87 | TicketPoolSize = 1024
88 | TicketsPerBlock = 5
89 | TicketMaturity = 16
90 | TicketExpiry = 6144 # 6*TicketPoolSize
91 | CoinbaseMaturity = 16
92 | SStxChangeMaturity = 1
93 | TicketPoolSizeWeight = 4
94 | StakeDiffAlpha = 1
95 | StakeDiffWindowSize = 144
96 | StakeDiffWindows = 20
97 | StakeVersionInterval = 144 * 2 * 7 # ~1 week
98 | MaxFreshStakePerBlock = 20 # 4*TicketsPerBlock
99 | StakeEnabledHeight = 16 + 16 # CoinbaseMaturity + TicketMaturity
100 | StakeValidationHeight = 768 # Arbitrary
101 | StakeBaseSigScript = (0x0000).to_bytes(2, byteorder="big")
102 | StakeMajorityMultiplier = 3
103 | StakeMajorityDivisor = 4
104 |
105 | # Decred organization related parameters.
106 | # Organization address is TcrypGAcGCRVXrES7hWqVZb5oLJKCZEtoL1.
107 | OrganizationPkScript = (0xA914D585CD7426D25B4EA5FAF1E6987AACFEDA3DB94287).to_bytes(
108 | 23, byteorder="big"
109 | )
110 | OrganizationPkScriptVersion = 0
111 |
112 | GENESIS_STAMP = 1533513600
113 | STAKE_SPLIT = 0.3
114 | POW_SPLIT = 0.6
115 | TREASURY_SPLIT = 0.1
116 |
117 | BlockOneSubsidy = int(100000 * 1e8)
118 |
--------------------------------------------------------------------------------
/decred/decred/dcr/wire/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/decred/decred/dcr/wire/__init__.py
--------------------------------------------------------------------------------
/decred/decred/dcr/wire/msgping.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | Based on dcrd MsgPing.
6 | """
7 |
8 | from decred.util.encode import ByteArray
9 |
10 |
11 | CmdPing = "ping"
12 |
13 | NonceLength = 8
14 |
15 |
16 | class MsgPing:
17 | """
18 | MsgPing implements the Message API and represents a Decred ping message.
19 |
20 | For versions BIP0031Version and earlier, it is used primarily to confirm
21 | that a connection is still valid. A transmission error is typically
22 | interpreted as a closed connection and that the peer should be removed.
23 | For versions AFTER BIP0031Version it contains an identifier which can be
24 | returned in the pong message to determine network timing.
25 |
26 | The payload for this message just consists of a nonce used for identifying
27 | it later.
28 | """
29 |
30 | def __init__(self, nonce):
31 | """
32 | Args:
33 | nonce (int): A value unique to each ping message.
34 | """
35 | self.nonce = nonce
36 |
37 | @staticmethod
38 | def btcDecode(b, pver):
39 | """
40 | btcDecode decodes b using the Decred protocol encoding into the
41 | receiver. This is part of the Message API.
42 |
43 | Args:
44 | b (ByteArray): The encoded MsgPing.
45 | pver (int): The protocol version. Unused.
46 |
47 | Returns:
48 | MsgPing: The MsgPing.
49 | """
50 | return MsgPing(nonce=b.unLittle().int())
51 |
52 | def btcEncode(self, pver):
53 | """
54 | btcEncode encodes the receiver using the Decred protocol encoding.
55 | This is part of the Message API.
56 |
57 | Args:
58 | pver (int): The protocol version. Unused.
59 |
60 | Returns:
61 | ByteArray: The encoded MsgPing
62 | """
63 | return ByteArray(self.nonce, length=8).littleEndian()
64 |
65 | @staticmethod
66 | def command():
67 | """
68 | The protocol command string for the message. This is part of the
69 | Message API.
70 |
71 | Returns:
72 | str: The command string.
73 | """
74 | return CmdPing
75 |
76 | @staticmethod
77 | def maxPayloadLength(pver):
78 | """
79 | The maximum length the payload can be for the receiver. This is part of
80 | the Message API.
81 |
82 | Args:
83 | pver (int): The protocol version. Unused.
84 |
85 | Returns:
86 | int: The maximum payload length.
87 | """
88 | return NonceLength
89 |
--------------------------------------------------------------------------------
/decred/decred/dcr/wire/msgpong.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | Based on dcrd MsgPing.
6 | """
7 |
8 | from decred.util.encode import ByteArray
9 |
10 |
11 | CmdPong = "pong"
12 |
13 | NonceLength = 8
14 |
15 |
16 | class MsgPong:
17 | """
18 | MsgPong implements the Message API and represents a Decred pong message
19 | which is used primarily to confirm that a connection is still valid in
20 | response to a Decred ping message (MsgPing).
21 |
22 | This message was not added until protocol versions AFTER BIP0031Version.
23 | """
24 |
25 | def __init__(self, nonce):
26 | """
27 | Args:
28 | nonce (int): Unique value associated with associated with a specific
29 | ping message.
30 | """
31 | self.nonce = nonce
32 |
33 | @staticmethod
34 | def btcDecode(b, pver):
35 | """
36 | btcDecode decodes b using the Decred protocol encoding into the receiver. This
37 | is part of the Message API.
38 |
39 | Args:
40 | b (ByteArray): The encoded MsgPong.
41 | pver (int): The protocol version. Unused.
42 |
43 | Returns:
44 | MsgPong. The MsgPong.
45 | """
46 | return MsgPong(nonce=b.unLittle().int())
47 |
48 | def btcEncode(self, pver):
49 | """
50 | btcEncode encodes the receiver using the Decred protocol encoding.
51 | This is part of the Message API.
52 |
53 | Args:
54 | pver (int): The protocol version. Unused.
55 |
56 | Returns:
57 | ByteArray: The encoded MsgPong
58 | """
59 | return ByteArray(self.nonce, length=8).littleEndian()
60 |
61 | @staticmethod
62 | def command():
63 | """
64 | command returns the protocol command string for the message. This is
65 | part of the Message API.
66 |
67 | Returns:
68 | str: The command string.
69 | """
70 | return CmdPong
71 |
72 | @staticmethod
73 | def maxPayloadLength(pver):
74 | """
75 | maxPayloadLength returns the maximum length the payload can be for the
76 | receiver. This is part of the Message API.
77 |
78 | Args:
79 | pver (int): The protocol version. Unused.
80 |
81 | Returns:
82 | int: The maximum payload length.
83 | """
84 | return NonceLength
85 |
--------------------------------------------------------------------------------
/decred/decred/dcr/wire/msgverack.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | Based on dcrd MsgVerAck.
6 | """
7 |
8 | from decred.util.encode import ByteArray
9 |
10 |
11 | CmdVerAck = "verack"
12 |
13 |
14 | class MsgVerAck:
15 | """
16 | MsgVerAck defines a Decred verack message which is used for a peer to
17 | acknowledge a version message (MsgVersion) after it has used the information
18 | to negotiate parameters. It implements the Message API.
19 |
20 | This message has no payload.
21 | """
22 |
23 | @staticmethod
24 | def btcDecode(b, pver):
25 | """
26 | Decode b using the Decred protocol encoding into the receiver. This is
27 | part of the Message API.
28 |
29 | Args:
30 | b (ByteArray): The encoded MsgVerAck. Unused, since MsgVerAck has
31 | a zero-length payload.
32 | pver (int): The protocol version. Unused.
33 | """
34 | return MsgVerAck()
35 |
36 | def btcEncode(self, pver):
37 | """
38 | btcEncode encodes the MsgVerAck using the Decred protocol encoding. This
39 | is part of the Message API.
40 |
41 | Args:
42 | pver (int): The protocol version. Unused.
43 |
44 | Returns:
45 | ByteArray: The encoded MsgVerAck.
46 | """
47 | return ByteArray()
48 |
49 | def command(self):
50 | """
51 | The protocol command string for the message. This is part of the
52 | Message API.
53 |
54 | Returns:
55 | str: The command string.
56 | """
57 | return CmdVerAck
58 |
59 | def maxPayloadLength(self, pver):
60 | """
61 | The maximum length the payload can be for the receiver. This is part of
62 | the Message API.
63 |
64 | Args:
65 | pver (int): The protocol version. Unused.
66 |
67 | Returns:
68 | int: The maximum payload length.
69 | """
70 | return 0
71 |
--------------------------------------------------------------------------------
/decred/decred/dcr/wire/netaddress.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import socket
7 | import time
8 |
9 | from decred import DecredError
10 | from decred.util.encode import ByteArray
11 |
12 |
13 | MaxNetAddressPayload = 30
14 |
15 | # Prefix for a IPv4 adderess encoded as 16 bytes.
16 | ipv4to16prefix = ByteArray(0xFFFF, length=12)
17 |
18 |
19 | class NetAddress:
20 | """
21 | NetAddress defines information about a peer on the network including the time
22 | it was last seen, the services it supports, its IP address, and port.
23 | """
24 |
25 | def __init__(self, ip, port, services, stamp=None):
26 | """
27 | Args:
28 | ip (str or bytes-like): The peer's IP address.
29 | port (int): Port the peer is using. This is encoded in big endian
30 | on the wire which differs from most everything else.
31 | services (int): Bitfield which identifies the services supported by
32 | the peer.
33 | stamp (int): Optional. Default: current time. The last time the peer
34 | was seen. This is, unfortunately, encoded as an int on the wire
35 | and therefore is limited to 2106. This field is not present in
36 | the Decred version message (MsgVersion) nor was it added until
37 | protocol version >= NetAddressTimeVersion.
38 | """
39 | self.timestamp = stamp if stamp else int(time.time())
40 | self.services = services
41 |
42 | # If the IP is a string, parse it to bytes.
43 | if isinstance(ip, str):
44 | ip = decodeStringIP(ip)
45 | self.ip = ip
46 |
47 | self.port = port
48 |
49 | def hasService(self, service):
50 | """
51 | Whether the specified service is supported by the address.
52 |
53 | Args:
54 | service (int): Bitfield which identifies the service(s) to check.
55 |
56 | Returns:
57 | bool: True if this peer offers the service(s).
58 | """
59 | return self.services & service == service
60 |
61 | def addService(self, service):
62 | """
63 | Adds service(s) as a supported service.
64 |
65 | Args:
66 | service (int): Bitfield which identifies the service(s) to add.
67 | """
68 | self.services |= service
69 |
70 |
71 | def readNetAddress(b, hasStamp):
72 | """
73 | Reads an encoded NetAddress from b depending on the protocol version and
74 | whether or not the timestamp is included per hasStamp. Some messages like
75 | version do not include the timestamp.
76 |
77 | Args:
78 | b (ByteArray): The encoded NetAddress.
79 | hasStamp (bool): Whether or not the NetAddress has a timestamp.
80 |
81 | Returns:
82 | NetAddress: The decoded NetAddress.
83 | """
84 | expLen = 30 if hasStamp else 26
85 | if len(b) != expLen:
86 | raise DecredError(
87 | f"readNetAddress wrong length (hasStamp={hasStamp}) expected {expLen}, got {len(b)}"
88 | )
89 |
90 | # NOTE: The Decred protocol uses a uint32 for the timestamp so it will
91 | # stop working somewhere around 2106. Also timestamp wasn't added until
92 | # protocol version >= NetAddressTimeVersion
93 | stamp = b.pop(4).unLittle().int() if hasStamp else 0
94 | services = b.pop(8).unLittle().int()
95 | ip = b.pop(16)
96 | if ip[:12] == ipv4to16prefix:
97 | ip = ip[12:]
98 |
99 | # Sigh. Decred protocol mixes little and big endian.
100 | port = b.pop(2).int()
101 |
102 | return NetAddress(ip=ip, port=port, services=services, stamp=stamp,)
103 |
104 |
105 | def writeNetAddress(netAddr, hasStamp):
106 | """
107 | writeNetAddress serializes a NetAddress depending on the protocol
108 | version and whether or not the timestamp is included per hasStamp. Some
109 | messages like version do not include the timestamp.
110 |
111 | Args:
112 | netAddr (NetAddress): The peer's NetAddress.
113 | hasStamp (bool): Whether to encode the timestamp.
114 |
115 | Returns:
116 | ByteArray: The encoded NetAddress.
117 | """
118 | # NOTE: The Decred protocol uses a uint32 for the timestamp so it will
119 | # stop working somewhere around 2106. Also timestamp wasn't added until
120 | # until protocol version >= NetAddressTimeVersion.
121 | b = (
122 | ByteArray(netAddr.timestamp, length=4).littleEndian()
123 | if hasStamp
124 | else ByteArray()
125 | )
126 |
127 | # Ensure to always write 16 bytes even if the ip is nil.
128 | ip = netAddr.ip
129 | if ip is None:
130 | ip = ByteArray(length=16)
131 | if len(ip) == 4:
132 | ip = ipv4to16prefix + ip
133 |
134 | b += ByteArray(netAddr.services, length=8).littleEndian()
135 | b += ByteArray(ip, length=16)
136 | b += ByteArray(netAddr.port, length=2)
137 |
138 | return b
139 |
140 |
141 | def decodeStringIP(ip):
142 | """
143 | Parse an IP string to bytes.
144 |
145 | Args:
146 | ip (str): The string-encoded IP address.
147 |
148 | Returns:
149 | bytes-like: The byte-encoded IP address.
150 | """
151 | try:
152 | return socket.inet_pton(socket.AF_INET, ip)
153 | except OSError:
154 | pass
155 | try:
156 | return socket.inet_pton(socket.AF_INET6, ip)
157 | except OSError:
158 | raise DecredError(f"failed to decode IP {ip}")
159 |
--------------------------------------------------------------------------------
/decred/decred/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/decred/decred/util/__init__.py
--------------------------------------------------------------------------------
/decred/decred/util/chains.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, The Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from decred import DecredError
7 | from decred.dcr import account as dcracct, nets as dcrnets
8 |
9 |
10 | class BipIDs:
11 | """
12 | BIP0044 IDs for supported assets.
13 | """
14 |
15 | decred = dcracct.BIPID
16 |
17 |
18 | """IDSymbols converts BIP0044 ID to a lower-case ticker symbol."""
19 | IDSymbols = {
20 | BipIDs.decred: "dcr",
21 | }
22 |
23 |
24 | """
25 | SymbolIDs is a Python dict mapping ticker symbols for supported assets to
26 | their BIP0044 ID.
27 | """
28 | SymbolIDs = {v: k for k, v in IDSymbols.items()}
29 |
30 |
31 | """
32 | AccountConstructors maps the asset's BIP0044 ID to the constructor for the
33 | Account object.
34 | """
35 | AccountConstructors = {
36 | BipIDs.decred: dcracct.Account,
37 | }
38 |
39 |
40 | """
41 | NetworkParams maps the asset's BIP0044 ID to a dict of network parameters.
42 | """
43 | NetworkParams = {
44 | BipIDs.decred: {
45 | "mainnet": dcrnets.mainnet,
46 | "testnet3": dcrnets.testnet,
47 | "simnet": dcrnets.simnet,
48 | }
49 | }
50 |
51 |
52 | def parseCoinType(coinType):
53 | """
54 | Parse the coin type. If coinType is a string, it will be converted to the
55 | BIP0044 ID. If it is already an integer, it is returned as is.
56 |
57 | Args:
58 | coinType (int or str): The asset. BIP0044 ID or ticker symbol.
59 |
60 | Returns:
61 | int: The BIP0044 ID.
62 | """
63 | if isinstance(coinType, str):
64 | ticker = coinType.lower()
65 | if ticker not in SymbolIDs:
66 | raise DecredError(f"ticker symbol {ticker} not found")
67 | coinType = SymbolIDs[ticker]
68 | if not isinstance(coinType, int):
69 | raise DecredError(f"unsupported type for coinType {type(coinType)}")
70 | return coinType
71 |
72 |
73 | _chains = {}
74 |
75 |
76 | def registerChain(coinType, chain):
77 | """
78 | Set the app-wide network parameters for a particular asset.
79 |
80 | Args:
81 | coinType (int or str): The asset. BIP0044 ID or ticker symbol.
82 | chain (obj): Network parameters.
83 | """
84 | _chains[parseCoinType(coinType)] = chain
85 |
86 |
87 | def chain(coinType):
88 | """
89 | Fetch the registered network parameters for an asset.
90 |
91 | Args:
92 | coinType (int or str): The asset. BIP0044 ID or ticker symbol.
93 |
94 | Returns:
95 | obj: Network parameters.
96 | """
97 | return _chains.get(parseCoinType(coinType))
98 |
--------------------------------------------------------------------------------
/decred/decred/util/tinyhttp.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | See LICENSE for details
4 |
5 | DcrdataClient.endpointList() for available enpoints.
6 | """
7 |
8 | import json
9 | from urllib.parse import urlencode
10 | import urllib.request as urlrequest
11 |
12 | from decred import DecredError
13 |
14 | from .helpers import formatTraceback
15 |
16 |
17 | def get(url, **kwargs):
18 | return request(url, **kwargs)
19 |
20 |
21 | def post(url, data, **kwargs):
22 | return request(url, data, **kwargs)
23 |
24 |
25 | def request(url, postData=None, headers=None, urlEncode=False, context=None):
26 | # GET method used when encoded data is None.
27 | encoded = None
28 | if postData:
29 | if urlEncode:
30 | # Encode the data in URL query string form.
31 | encoded = urlencode(postData).encode("utf-8")
32 | else:
33 | # Encode the data as JSON.
34 | encoded = json.dumps(postData).encode("utf-8")
35 |
36 | headers = headers if headers else {}
37 | req = urlrequest.Request(url, headers=headers, data=encoded)
38 |
39 | try:
40 | raw = urlrequest.urlopen(req, context=context).read().decode()
41 | except Exception as err:
42 | raise DecredError(f"Error in requesting URL {url}: {formatTraceback(err)}")
43 |
44 | try:
45 | # Try to decode the response as JSON, but fall back to just
46 | # returning the string.
47 | return json.loads(raw)
48 | except json.JSONDecodeError:
49 | return raw
50 |
--------------------------------------------------------------------------------
/decred/decred/util/ws.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import threading
7 |
8 | import websocket
9 |
10 |
11 | class Client(websocket.WebSocketApp):
12 | """
13 | A websocket client. On top of the base functionality offered by
14 | websocket.WebSocketApp, Client handles some initialization and shutdown
15 | related tasks.
16 | """
17 |
18 | def __init__(self, url, certPath=None, **k):
19 | """
20 | Args:
21 | url str: The websocket server URL.
22 |
23 | Additional keyword arguments are passed directly to the WebSocketApp
24 | constructor. The caller should provide callback functions for various
25 | events. The most common callbacks are listed here.
26 |
27 | on_open: Called at opening websocket. This function has one
28 | argument, the instance of Client.
29 |
30 | on_message: Called when data is received. on_message has 2
31 | arguments. The 1st argument is the instance of Client. The 2nd
32 | argument is the received message decoded (utf-8) as a str.
33 |
34 | on_error: Called when an exception is encountered. on_error has 2
35 | arguments. The 1st argument is the instance of Client. The 2nd
36 | argument is an Exception.
37 |
38 | on_close: Called when the connection is closed. This function has
39 | one argument, the instance of Client.
40 | """
41 | # The constructor will block until the initEvent is set so that the
42 | # Client is immediately usable by the caller.
43 | initEvent = threading.Event()
44 |
45 | cleanURL = url.replace("https:", "wss:").replace("http:", "ws:")
46 |
47 | user_open = k.pop("on_open", None)
48 |
49 | def on_open(ws):
50 | if user_open:
51 | user_open(ws)
52 | initEvent.set()
53 |
54 | user_close = k.pop("on_close", None)
55 |
56 | def on_close(ws):
57 | # Some initialization errors won't call on_open, but they will call
58 | # on_close, so set the initEvent here too.
59 | if user_close:
60 | user_close(ws)
61 | initEvent.set()
62 |
63 | super().__init__(cleanURL, on_open=on_open, on_close=on_close, **k)
64 |
65 | sslopt = {"ca_certs": certPath} if certPath else None
66 |
67 | self.thread = threading.Thread(
68 | None, self.run_forever, kwargs={"sslopt": sslopt}
69 | )
70 | self.thread.start()
71 | initEvent.wait()
72 |
73 | def close(self):
74 | """
75 | Close the connection and wait for shutdown.
76 | """
77 | super().close()
78 | if self.thread:
79 | self.thread.join(20)
80 | self.thread = None
81 |
--------------------------------------------------------------------------------
/decred/decred/wallet/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/decred/decred/wallet/__init__.py
--------------------------------------------------------------------------------
/decred/examples/create_testnet_wallet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, The Decred developers
3 |
4 | This example script will prompt for a password and create a password-encrypted
5 | testnet wallet. The mnemonic seed and an address are printed.
6 | """
7 |
8 | from getpass import getpass
9 |
10 | from decred.wallet.wallet import SimpleWallet
11 |
12 |
13 | def main():
14 | # Create an encrypted, password-protected wallet file.
15 | password = getpass()
16 | walletDir = "wallets"
17 | print("Creating and synchronizing wallet")
18 | wallet, words = SimpleWallet.create(walletDir, password, "testnet")
19 |
20 | # Print the seed words and an address.
21 | try:
22 | print("Mnemonic seed\n-------------")
23 | print(" ".join(words))
24 | print("Receive DCR at %s" % wallet.currentAddress())
25 | finally:
26 | wallet.close()
27 |
28 |
29 | if __name__ == "__main__":
30 | main()
31 |
--------------------------------------------------------------------------------
/decred/examples/plot_ticket_price.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, The Decred developers
3 |
4 | This example script will pull ticket price data from dcrdata and plot using
5 | matplotlib. The matplotlib package is not a decred dependency, so it should
6 | be installed separately with `pip3 install matplotlib`.
7 | """
8 |
9 | from decred.dcr.dcrdata import DcrdataClient
10 | from decred.util.helpers import mktime
11 |
12 |
13 | try:
14 | from matplotlib import pyplot as plt
15 | except ImportError:
16 | print("matplotlib import error. Did you 'pip3 install matplotlib'?")
17 | exit()
18 |
19 |
20 | def main():
21 | # Create a dcrdata client and grab the ticket price data.
22 | dcrdata = DcrdataClient("https://dcrdata.decred.org")
23 | ticketPrice = dcrdata.chart("ticket-price")
24 | # ticketPrice["t"] is UNIX timestamp (was "x")
25 | # ticketPrice["price"] is ticket price, in atoms (was "y")
26 | # These keys changed, see https://github.com/decred/dcrdata/pull/1507
27 |
28 | # Make the axes pretty.
29 | ax = plt.gca() # gca = get current axes
30 | years = range(2016, 2026)
31 | ax.set_xticks([mktime(year) for year in years])
32 | ax.set_xticklabels([str(year) for year in years])
33 | ax.set_xlabel("date")
34 | ax.set_ylabel("ticket price (DCR)")
35 |
36 | ax.plot(
37 | ticketPrice["t"],
38 | [atoms * 1e-8 for atoms in ticketPrice["price"]],
39 | color="#222222",
40 | )
41 | plt.show()
42 |
43 |
44 | if __name__ == "__main__":
45 | main()
46 |
--------------------------------------------------------------------------------
/decred/examples/send_testnet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, The Decred developers
3 |
4 | This example script will send 1 DCR from a wallet as created with the
5 | create_testnet_wallet.py example script to the return address from the testnet
6 | faucet at https://faucet.decred.org/.
7 | Before running this script, send the wallet some DCR from the faucet.
8 | """
9 |
10 | from getpass import getpass
11 |
12 | from decred.wallet.wallet import SimpleWallet
13 |
14 | # Testnet return address for faucet.decred.org.
15 | TESTNET_ADDRESS = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"
16 |
17 |
18 | def main():
19 | value = int(1 * 1e8) # 1 DCR, atoms
20 | password = getpass()
21 | walletDir = "wallets"
22 | try:
23 | print("Opening and synchronizing wallet")
24 | wallet = SimpleWallet(walletDir, password, "testnet")
25 | except Exception as e:
26 | print("Failed to open wallet with provided password: %s" % e)
27 | exit()
28 |
29 | try:
30 | # Send some DCR.
31 | tx = wallet.sendToAddress(value, TESTNET_ADDRESS)
32 | # Print the transaction ID and a dcrdata link.
33 | print("Transaction ID: %s" % tx.id())
34 | print("See transaction at https://testnet.dcrdata.org/tx/%s" % tx.id())
35 | except Exception as e:
36 | print("Failed to send transaction: %s" % e)
37 | finally:
38 | wallet.close()
39 |
40 |
41 | if __name__ == "__main__":
42 | main()
43 |
--------------------------------------------------------------------------------
/decred/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "decred"
3 | version = "0.1.2-dev"
4 | description = "A Python 3 Decred toolkit."
5 | license = "ISC"
6 | homepage = "https://decred.org/"
7 | repository = "https://github.com/decred/tinydecred/"
8 | documentation = "https://github.com/decred/tinydecred/blob/master/decred"
9 | authors = [
10 | "Brian Stafford ",
11 | "The Decred developers "
12 | ]
13 | classifiers = [
14 | "Development Status :: 3 - Alpha",
15 | "Intended Audience :: Developers",
16 | "License :: OSI Approved :: ISC License (ISCL)",
17 | "Operating System :: OS Independent",
18 | "Programming Language :: Python :: 3.6",
19 | "Programming Language :: Python :: 3.7",
20 | "Programming Language :: Python :: 3.8",
21 | "Topic :: Office/Business :: Financial"
22 | ]
23 | build = "build.py"
24 |
25 | [tool.poetry.dependencies]
26 | python = "^3.6"
27 | appdirs = "^1.4.3"
28 | base58 = "^2.0.0"
29 | blake256 = "^0.1.1"
30 | pynacl = "^1.3.0"
31 | websocket_client = "^0.57.0"
32 |
33 | [tool.poetry.dev-dependencies]
34 | pytest = "^5.3"
35 | pytest-cov = "^2.8"
36 | pytest-pudb = "^0.7.0"
37 | flake8 = "^3.7"
38 | black = "^19.10b0"
39 | isort = "^4.3"
40 | websocket-server = "^0.4"
41 | pytest-benchmark = "^3.2.3"
42 | pytest-profiling = "^1.7.0"
43 | Cython = "^0.29.16"
44 |
45 | [tool.isort]
46 | atomic = "true"
47 | combine_as_imports = "true"
48 | combine_star = "true"
49 | filter_files = "true"
50 | force_grid_wrap = 0
51 | force_sort_within_sections = "true"
52 | include_trailing_comma = "true"
53 | line_length = 88
54 | lines_after_imports = 2
55 | multi_line_output = 3
56 | use_parentheses = "true"
57 | virtual_env = "./.venv/"
58 | skip = [
59 | "./examples/create_testnet_wallet.py",
60 | "./examples/send_testnet.py"
61 | ]
62 |
63 | [build-system]
64 | requires = ["setuptools", "poetry>=1.0.3"]
65 | build-backend = "poetry.masonry.api"
66 |
--------------------------------------------------------------------------------
/decred/tests/benchmark/crypto/secp256k1/test_field_benchmark.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from decred.crypto.secp256k1 import field
7 |
8 |
9 | H1 = "b3d9aac9c5e43910b4385b53c7e78c21d4cd5f8e683c633aed04c233efc2e120"
10 | H2 = "b0ba920360ea8436a216128047aab9766d8faf468895eb5090fc8241ec758896"
11 |
12 |
13 | class Test_FieldVal:
14 | def test_normalize(self, benchmark):
15 | f = field.FieldVal()
16 | f.n = [0xFFFFFFFF, 0xFFFFFFC0, 0xFC0, 0, 0, 0, 0, 0, 0, 0]
17 | benchmark(f.normalize)
18 |
19 | def test_negate(self, benchmark):
20 | f = field.FieldVal.fromHex(H1)
21 | benchmark(f.negate, 1)
22 |
23 | def test_add(self, benchmark):
24 | f1 = field.FieldVal.fromHex(H1)
25 | f2 = field.FieldVal.fromHex(H2)
26 | benchmark(f1.add, f2)
27 |
28 | def test_add2(self, benchmark):
29 | f1 = field.FieldVal.fromHex(H1)
30 | f2 = field.FieldVal.fromHex(H2)
31 | benchmark(f1.add2, f1, f2)
32 |
33 | def test_square(self, benchmark):
34 | f = field.FieldVal.fromHex(H1)
35 | benchmark(f.square)
36 |
37 | def test_mul(self, benchmark):
38 | f = field.FieldVal.fromHex(H1)
39 | f2 = field.FieldVal.fromHex(H2)
40 | benchmark(f.mul, f2)
41 |
42 | def test_inverse(self, benchmark):
43 | f = field.FieldVal.fromHex(H1)
44 | benchmark(f.inverse)
45 |
--------------------------------------------------------------------------------
/decred/tests/benchmark/util/test_database_benchmark.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred.util import database
9 |
10 |
11 | @pytest.fixture
12 | def setup_db(tmpdir):
13 | # Open a key value db in the temp directory.
14 | db = database.KeyValueDatabase(tmpdir.join("bm.sqlite")).child("testdb")
15 | # Generate some data.
16 | return db, [(str(i).encode(), str(i).encode()) for i in range(10)]
17 |
18 |
19 | def test_insert(setup_db, benchmark):
20 | def insert(db, data):
21 | for k, v in data:
22 | db[k] = v
23 |
24 | db, data = setup_db
25 | benchmark(insert, db, data)
26 | assert len(db) == len(data)
27 |
28 |
29 | def test_clear(setup_db, benchmark):
30 | db, data = setup_db
31 | db.batchInsert(data)
32 | benchmark(db.clear)
33 | assert len(db) == 0
34 |
35 |
36 | def test_batchInsert(setup_db, benchmark):
37 | db, data = setup_db
38 | benchmark(db.batchInsert, data)
39 | assert len(db) == len(data)
40 |
--------------------------------------------------------------------------------
/decred/tests/conftest.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import os
7 | import random
8 |
9 | import pytest
10 |
11 | from decred.util import chains, helpers
12 |
13 |
14 | @pytest.fixture
15 | def sign():
16 | def _sign(x):
17 | """
18 | x: number
19 | """
20 | if x < 0:
21 | return -1
22 | elif x > 0:
23 | return 1
24 | else:
25 | return 0
26 |
27 | return _sign
28 |
29 |
30 | # Seed initialization is delegated to tests.
31 | # random.seed(0)
32 |
33 |
34 | @pytest.fixture
35 | def randBytes():
36 | def _randBytes(low=0, high=50):
37 | return bytes(random.randint(0, 255) for _ in range(random.randint(low, high)))
38 |
39 | return _randBytes
40 |
41 |
42 | @pytest.fixture(scope="module")
43 | def prepareLogger(request):
44 | helpers.prepareLogging()
45 |
46 |
47 | @pytest.fixture(scope="class")
48 | def registerChain(request):
49 | chains.registerChain("dcr", None)
50 |
51 |
52 | @pytest.fixture(scope="session")
53 | def dcrdConfig():
54 | dcrdCfgDir = helpers.appDataDir("dcrd")
55 | cfgPath = os.path.join(dcrdCfgDir, "dcrd.conf")
56 | if not os.path.isfile(cfgPath):
57 | return None
58 | cfg = helpers.readINI(cfgPath, ["rpcuser", "rpcpass", "rpccert"])
59 | assert "rpcuser" in cfg
60 | assert "rpcpass" in cfg
61 | if "rpccert" not in cfg:
62 | cfg["rpccert"] = os.path.join(dcrdCfgDir, "rpc.cert")
63 | if "rpclisten" not in cfg:
64 | cfg["rpclisten"] = "localhost:9109"
65 | return cfg
66 |
--------------------------------------------------------------------------------
/decred/tests/integration/dcr/test_blockchain_live.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred.dcr.blockchain import LocalNode
9 | from decred.dcr.nets import mainnet
10 | from decred.dcr.wire.msgtx import OutPoint
11 | from decred.util.encode import ByteArray, rba
12 |
13 |
14 | @pytest.fixture
15 | def node(dcrdConfig, tmp_path):
16 | if dcrdConfig is None:
17 | return "no configuration found"
18 | try:
19 | bc = LocalNode(
20 | netParams=mainnet,
21 | dbPath=tmp_path / "tmp.db",
22 | url="https://" + dcrdConfig["rpclisten"],
23 | user=dcrdConfig["rpcuser"],
24 | pw=dcrdConfig["rpcpass"],
25 | certPath=dcrdConfig["rpccert"],
26 | )
27 | except Exception as e:
28 | return e
29 | yield bc
30 | bc.close()
31 |
32 |
33 | def test_syncAccount(node):
34 | # sync a small range of headers.
35 | node.syncHeaderRange(400000, 400100)
36 |
37 | # An address from block 400,051, a split transaction to fund a ticket in
38 | # block 400,053
39 | addr51_0out = "DsSMy2pPZmS7Jd9QoGbzX2DfhAJDqwftWTR"
40 | # An address from a previous outpoint being spent in block 400,074. Also
41 | # has an output in block 400,053.
42 | addr74_0in = "DsbZZXdkA4JKBUK6YbqGmDkgDryKBVdGVD5"
43 | # A ticket being spent in transaction 400,025
44 | ticket = OutPoint(
45 | txHash=rba("60d97229b6229923d6c4c3c6f290fbc507833186f3d1eed1e603e205e0dfe493"),
46 | idx=0,
47 | tree=1,
48 | )
49 |
50 | startHash = node.mainchainDB.first()[1]
51 | print("startHash", startHash.rhex())
52 |
53 | scanBlocks = node.syncAccount(
54 | addrs=[addr51_0out, addr74_0in], outPoints=[ticket], startHash=startHash,
55 | )
56 |
57 | # A mapping of block hashes to number of transactions expected.
58 | expTxs = {
59 | rba("00000000000000000f9aed681de1bb5ee8d0022ec427d2df526534eab4675c59"): 1,
60 | rba("000000000000000015749883c3cf975ea3565695a833e6f44a5caabf8e132ff3"): 1,
61 | rba("000000000000000002e6de7314fe8062e1f8abf0711b8cf1f026aff0876466b6"): 2,
62 | rba("0000000000000000097661b13a8d4d8d99753445b21f91ab53f66bdbc088338c"): 1,
63 | }
64 |
65 | assert len(scanBlocks) == len(expTxs)
66 | for block in scanBlocks:
67 | assert block.hash in expTxs
68 | assert expTxs[block.hash] == len(block.txs)
69 |
70 |
71 | def test_syncHeaders(node):
72 | # Get the current best tip.
73 | tip = node.rpc.getBestBlock()
74 | # Go back 10 blocks, inclusive of ends, so 11 blocks total to sync.
75 | rootHash = node.rpc.getBlockHash(tip.height - 10)
76 | rootHeight = node.rpc.getBlock(rootHash).height
77 |
78 | synced, orphans = node.syncHeaders(rootHash)
79 | firstHeight, firstHash = node.mainchainDB.first()
80 | assert firstHeight == rootHeight
81 | assert firstHash == rootHash
82 | assert len(node.headerDB) >= 11
83 | assert len(node.mainchainDB) >= 11
84 | assert synced >= 11
85 | assert len(orphans) == 0
86 | for height, blockHash in node.mainchainDB.items():
87 | assert node.rpc.getBlock(blockHash).height == height
88 |
89 | # Insert a wrong hash at the tip height
90 | node.mainchainDB[tip.height] = ByteArray(length=32)
91 |
92 | # Start with an earlier root
93 | newRootHash = node.rpc.getBlockHash(rootHeight - 2)
94 |
95 | preSyncHeight = node.mainchainDB.last()[0]
96 | synced, orphans = node.syncHeaders(newRootHash)
97 | # Expect 3 to be synced. 2 from the beginning, 1 from the reorg.
98 | assert synced == node.mainchainDB.last()[0] - preSyncHeight + 3
99 | assert len(orphans) == 1
100 |
101 | # Clear the database and test syncing from genesis and more than
102 | # maxBlocksPerRescan, just to exercise the paths.
103 | node.mainchainDB.clear()
104 | node.syncHeaderRange(0, 3000)
105 | assert len(node.mainchainDB) == 3000
106 |
--------------------------------------------------------------------------------
/decred/tests/integration/dcr/test_vsp_live.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from decred.dcr import vsp
7 | from decred.dcr.nets import testnet
8 |
9 |
10 | VSP_URL = "https://dcrstakedinner.com"
11 | API_KEY = (
12 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Nzc0MzM0NDIsIm"
13 | "lzcyI6Imh0dHBzOi8vd3d3LmRjcnN0YWtlZGlubmVyLmNvbSIsImxvZ2dlZEluQ"
14 | "XMiOjQ2fQ.PEb000_TjQuBYxjRdh-VOaXMdV2GUw3_ZyIyp_tfpFE"
15 | )
16 | # Signing address is needed to validate server-reported redeem script.
17 | SIGNING_ADDRESS = "TkKmVKG7u7PwhQaYr7wgMqBwHneJ2cN4e5YpMVUsWSopx81NFXEzK"
18 |
19 |
20 | def test_vsp_live():
21 | the_vsp = vsp.VotingServiceProvider(VSP_URL, API_KEY, testnet.Name)
22 | the_vsp.authorize(SIGNING_ADDRESS)
23 | the_vsp.getStats()
24 | purc_info = the_vsp.getPurchaseInfo()
25 | # Test voting.
26 | if purc_info.voteBits & (1 << 1) != 0:
27 | nextVote = 1 | (1 << 2)
28 | else:
29 | nextVote = 1 | (1 << 1)
30 | the_vsp.setVoteBits(nextVote)
31 | purc_info = the_vsp.getPurchaseInfo()
32 | assert purc_info.voteBits == nextVote
33 |
--------------------------------------------------------------------------------
/decred/tests/integration/util/test_ws.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from queue import Queue
7 | import threading
8 | import time
9 | from urllib.parse import urlunsplit
10 |
11 | import pytest
12 | import websocket
13 | from websocket_server import WebsocketServer
14 |
15 | from decred.util import ws
16 |
17 |
18 | HOST = "localhost"
19 | PORT = 53791
20 | URL = urlunsplit(("http", f"{HOST}:{PORT}", "/", "", ""))
21 | QUIT = "QUIT"
22 | HELLO = "HELLO"
23 | YO = "YO"
24 | CLOSE_HANDSHAKE = bytearray([0x88, 0])
25 |
26 |
27 | def test_websocket():
28 | """
29 | Start a ws.Client and check basic functionality.
30 | """
31 | serverQ = Queue(1)
32 | clientQ = Queue(1)
33 | errorQ = Queue(1)
34 |
35 | def receive(client, server, message):
36 | if message == HELLO:
37 | server.send_message(client, YO)
38 | elif message == QUIT:
39 | client["handler"].request.send(CLOSE_HANDSHAKE)
40 | server.shutdown()
41 | serverQ.put(message)
42 |
43 | server = WebsocketServer(PORT, HOST)
44 | server.set_fn_message_received(receive)
45 | serverThread = threading.Thread(None, server.run_forever)
46 | serverThread.start()
47 | time.sleep(1)
48 |
49 | class test:
50 | was_opened = False
51 | was_closed = False
52 |
53 | def on_open(ws):
54 | test.was_opened = True
55 |
56 | def on_message(ws, msg):
57 | clientQ.put(msg, timeout=1)
58 |
59 | def on_close(ws):
60 | test.was_closed = True
61 |
62 | def on_error(ws, error):
63 | errorQ.put(error, timeout=1)
64 |
65 | def client(url):
66 | return ws.Client(
67 | url=url,
68 | on_open=on_open,
69 | on_message=on_message,
70 | on_close=on_close,
71 | on_error=on_error,
72 | )
73 |
74 | try:
75 | # Send a hello and make sure it is received.
76 | cl = client(URL)
77 | assert test.was_opened
78 |
79 | cl.send(HELLO)
80 | msg = serverQ.get(timeout=1)
81 | assert msg == HELLO
82 |
83 | # Make sure we got the response.
84 | msg = clientQ.get(timeout=1)
85 | assert msg == YO
86 |
87 | # Trigger a server shutdown.
88 | cl.send(QUIT)
89 | serverQ.get(timeout=1)
90 |
91 | # Make sure the close callback was called.
92 | assert test.was_closed
93 |
94 | # Make sure a send fails.
95 | with pytest.raises(websocket.WebSocketConnectionClosedException):
96 | cl.send("should be closed")
97 |
98 | # Force a client error and ensure it comes through the callback.
99 | assert errorQ.empty()
100 | cl = client("notaurl")
101 | errorQ.get(timeout=1)
102 |
103 | except Exception as e:
104 | server.shutdown()
105 | raise e
106 | finally:
107 | cl.close()
108 |
109 | serverThread.join()
110 |
--------------------------------------------------------------------------------
/decred/tests/integration/wallet/test_wallet.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | """
4 |
5 | from decred.dcr import nets
6 | from decred.wallet.wallet import SimpleWallet, Wallet
7 |
8 |
9 | PASSWORD = "test_password"
10 | NET_NAME = "testnet"
11 |
12 | # Testnet return address for faucet.decred.org.
13 | TESTNET_ADDRESS = "TsfDLrRkk9ciUuwfp2b8PawwnukYD7yAjGd"
14 |
15 |
16 | def test_SimpleWallet(tmp_path):
17 | wallet, _ = SimpleWallet.create(tmp_path, PASSWORD, NET_NAME)
18 | wallet.close()
19 | wallet = SimpleWallet(tmp_path, PASSWORD, NET_NAME)
20 | wallet.close()
21 |
22 |
23 | def test_Wallet(tmp_path):
24 | first_wallet_path = tmp_path / "first_wallet"
25 | netParams = nets.parse(NET_NAME)
26 | words, _ = Wallet.create(first_wallet_path, PASSWORD, netParams)
27 | second_wallet_path = tmp_path / "second_wallet"
28 | Wallet.createFromMnemonic(words, second_wallet_path, PASSWORD, netParams)
29 |
--------------------------------------------------------------------------------
/decred/tests/test_examples.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, The Decred developers
3 | """
4 |
5 | import importlib.util
6 | import os
7 | from pathlib import Path
8 | import py_compile
9 |
10 |
11 | def test_compile():
12 | exampleDir = Path(__file__).resolve().parent.parent / "examples"
13 |
14 | for filename in os.listdir(exampleDir):
15 | # The "plot_ticket_price.py" file imports matplotlib, which is not a TD
16 | # dependency, so skip that file.
17 | if not filename.endswith(".py") or "plot_" in filename:
18 | continue
19 | path = Path(exampleDir) / filename
20 | assert py_compile.compile(path) is not None
21 | spec = importlib.util.spec_from_file_location(filename.split(".")[0], path)
22 | m = importlib.util.module_from_spec(spec)
23 | spec.loader.exec_module(m)
24 |
--------------------------------------------------------------------------------
/decred/tests/unit/btc/test_nets_btc.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.btc import nets
10 |
11 |
12 | def test_nets():
13 | assert nets.parse("mainnet") is nets.mainnet
14 |
15 | assert nets.normalizeName("testnet3") == "testnet"
16 |
17 | with pytest.raises(DecredError):
18 | nets.parse("nonet")
19 |
--------------------------------------------------------------------------------
/decred/tests/unit/crypto/test_mnemonic.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.crypto import mnemonic
10 | from decred.util.encode import ByteArray
11 |
12 |
13 | class TestMnemonic:
14 | def test_all(self):
15 | # fmt: off
16 | tests = [
17 | (
18 | "topmost Istanbul Pluto vagabond treadmill Pacific brackish dictator"
19 | " goldfish Medusa afflict bravado chatter revolver Dupont midsummer"
20 | " stopwatch whimsical cowbell bottomless",
21 | ByteArray([
22 | 0xE5, 0x82, 0x94, 0xF2, 0xE9, 0xA2, 0x27, 0x48,
23 | 0x6E, 0x8B, 0x06, 0x1B, 0x31, 0xCC, 0x52, 0x8F,
24 | 0xD7, 0xFA, 0x3F, 0x19
25 | ]),
26 | ),
27 | (
28 | "stairway souvenir flytrap recipe adrift upcoming artist positive"
29 | " spearhead Pandora spaniel stupendous tonic concurrent transit Wichita"
30 | " lockup visitor flagpole escapade",
31 | ByteArray([
32 | 0xD1, 0xD4, 0x64, 0xC0, 0x04, 0xF0, 0x0F, 0xB5,
33 | 0xC9, 0xA4, 0xC8, 0xD8, 0xE4, 0x33, 0xE7, 0xFB,
34 | 0x7F, 0xF5, 0x62, 0x56
35 | ]),
36 | ),
37 | ]
38 | # fmt: on
39 | listToLower = lambda l: [x.lower() for x in l]
40 | for i, (words, seed) in enumerate(tests):
41 | unWords = mnemonic.encode(seed)
42 | assert listToLower(unWords[: len(unWords) - 1]) == listToLower(
43 | words.split()
44 | )
45 | unSeed = mnemonic.decode(words.split())
46 | assert seed == unSeed
47 |
48 | def test_bad_paths(self):
49 | wordlists = (["", "meme"], ["acme", "kiwi"])
50 | with pytest.raises(DecredError):
51 | for wordlist in wordlists:
52 | mnemonic.decode(wordlist)
53 |
--------------------------------------------------------------------------------
/decred/tests/unit/crypto/test_rando.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.crypto import rando
10 |
11 |
12 | def test_checkSeedLength():
13 | with pytest.raises(DecredError):
14 | rando.checkSeedLength(rando.MinSeedBytes - 1)
15 | assert rando.checkSeedLength(rando.MinSeedBytes) is None
16 | assert rando.checkSeedLength(rando.HASH_SIZE) is None
17 | assert rando.checkSeedLength(rando.KEY_SIZE) is None
18 | assert rando.checkSeedLength(rando.MaxSeedBytes) is None
19 | with pytest.raises(DecredError):
20 | rando.checkSeedLength(rando.MaxSeedBytes + 1)
21 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/conftest.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred.util import tinyhttp
9 |
10 |
11 | @pytest.fixture
12 | def http_get_post(monkeypatch):
13 | """
14 | Tests will use the returned "queue" function to add responses they want
15 | returned from both tinyhttp "get" and "post" functions.
16 | The "get" responses will use "url" as a key, while the "post" responses
17 | will use "(url, repr(data))".
18 | """
19 | q = {}
20 |
21 | def mock_get(url, **kwargs):
22 | return q[url].pop()
23 |
24 | def mock_post(url, data, **kwargs):
25 | return q[(url, repr(data))].pop()
26 |
27 | monkeypatch.setattr(tinyhttp, "get", mock_get)
28 | monkeypatch.setattr(tinyhttp, "post", mock_post)
29 |
30 | def queue(k, v):
31 | q.setdefault(k, []).append(v)
32 |
33 | return queue
34 |
35 |
36 | @pytest.fixture
37 | def MockWebSocketClient():
38 | class MockWebSocketClient_inner:
39 | def __init__(self, **kargs):
40 | self.on_message = kargs["on_message"]
41 | self.on_close = kargs["on_close"]
42 | self.on_error = kargs["on_error"]
43 | self.sent = []
44 | self.emitted = []
45 |
46 | def send(self, msg):
47 | self.sent.append(msg)
48 |
49 | def close(self):
50 | self.on_close(self)
51 |
52 | def emit(self, msg):
53 | self.emitted.append(msg)
54 |
55 | return MockWebSocketClient_inner
56 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/test-data/dcrdata.json:
--------------------------------------------------------------------------------
1 | [
2 | "/",
3 | "/address",
4 | "/address/{address}",
5 | "/address/{address}/",
6 | "/address/{address}/amountflow/{chartgrouping}",
7 | "/address/{address}/count/{N}",
8 | "/address/{address}/count/{N}/",
9 | "/address/{address}/count/{N}/raw",
10 | "/address/{address}/count/{N}/skip/{M}",
11 | "/address/{address}/count/{N}/skip/{M}/",
12 | "/address/{address}/count/{N}/skip/{M}/raw",
13 | "/address/{address}/raw",
14 | "/address/{address}/totals",
15 | "/address/{address}/types/{chartgrouping}",
16 | "/agenda",
17 | "/agenda/{agendaId}",
18 | "/agendas",
19 | "/agendas/",
20 | "/block",
21 | "/block/best",
22 | "/block/best/",
23 | "/block/best/hash",
24 | "/block/best/header",
25 | "/block/best/header/",
26 | "/block/best/header/raw",
27 | "/block/best/height",
28 | "/block/best/pos",
29 | "/block/best/raw",
30 | "/block/best/size",
31 | "/block/best/subsidy",
32 | "/block/best/tx",
33 | "/block/best/tx/",
34 | "/block/best/tx/count",
35 | "/block/best/verbose",
36 | "/block/hash/{blockhash}",
37 | "/block/hash/{blockhash}/",
38 | "/block/hash/{blockhash}/header",
39 | "/block/hash/{blockhash}/header/",
40 | "/block/hash/{blockhash}/header/raw",
41 | "/block/hash/{blockhash}/height",
42 | "/block/hash/{blockhash}/pos",
43 | "/block/hash/{blockhash}/raw",
44 | "/block/hash/{blockhash}/size",
45 | "/block/hash/{blockhash}/subsidy",
46 | "/block/hash/{blockhash}/tx",
47 | "/block/hash/{blockhash}/tx/",
48 | "/block/hash/{blockhash}/tx/count",
49 | "/block/hash/{blockhash}/verbose",
50 | "/block/range/{idx0}/{idx}",
51 | "/block/range/{idx0}/{idx}/",
52 | "/block/range/{idx0}/{idx}/size",
53 | "/block/range/{idx0}/{idx}/{step}",
54 | "/block/range/{idx0}/{idx}/{step}/",
55 | "/block/range/{idx0}/{idx}/{step}/size",
56 | "/block/{idx}",
57 | "/block/{idx}/",
58 | "/block/{idx}/hash",
59 | "/block/{idx}/header",
60 | "/block/{idx}/header/",
61 | "/block/{idx}/header/raw",
62 | "/block/{idx}/pos",
63 | "/block/{idx}/raw",
64 | "/block/{idx}/size",
65 | "/block/{idx}/subsidy",
66 | "/block/{idx}/tx",
67 | "/block/{idx}/tx/",
68 | "/block/{idx}/tx/count",
69 | "/block/{idx}/verbose",
70 | "/chart",
71 | "/chart/market/{token}",
72 | "/chart/market/{token}/candlestick/{bin}",
73 | "/chart/market/{token}/depth",
74 | "/chart/{charttype}",
75 | "/exchanges",
76 | "/exchanges/",
77 | "/exchanges/codes",
78 | "/mempool",
79 | "/mempool/",
80 | "/mempool/sstx",
81 | "/mempool/sstx/",
82 | "/mempool/sstx/details",
83 | "/mempool/sstx/details/{N}",
84 | "/mempool/sstx/fees",
85 | "/mempool/sstx/fees/{N}",
86 | "/proposal",
87 | "/proposal/{token}",
88 | "/stake",
89 | "/stake/diff",
90 | "/stake/diff/",
91 | "/stake/diff/b/{idx}",
92 | "/stake/diff/current",
93 | "/stake/diff/estimates",
94 | "/stake/diff/r/{idx0}/{idx}",
95 | "/stake/pool",
96 | "/stake/pool/",
97 | "/stake/pool/b/{idx}",
98 | "/stake/pool/b/{idxorhash}/full",
99 | "/stake/pool/full",
100 | "/stake/pool/r/{idx0}/{idx}",
101 | "/stake/powerless",
102 | "/stake/vote",
103 | "/stake/vote/info",
104 | "/status",
105 | "/status/happy",
106 | "/supply",
107 | "/ticketpool",
108 | "/ticketpool/",
109 | "/ticketpool/bydate/{tp}",
110 | "/ticketpool/charts",
111 | "/tx",
112 | "/tx/decoded/{txid}",
113 | "/tx/hex/{txid}",
114 | "/tx",
115 | "/tx/{txid}",
116 | "/tx/{txid}/",
117 | "/tx/{txid}/in",
118 | "/tx/{txid}/in/",
119 | "/tx/{txid}/in/{txinoutindex}",
120 | "/tx/{txid}/out",
121 | "/tx/{txid}/out/",
122 | "/tx/{txid}/out/{txinoutindex}",
123 | "/tx/{txid}/tinfo",
124 | "/tx/{txid}/trimmed",
125 | "/tx/{txid}/vinfo",
126 | "/txs",
127 | "/txs/",
128 | "/txs/trimmed"
129 | ]
130 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/test_agenda.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from decred.dcr.agenda import Agenda, AgendaChoices, AgendaInfo, AgendasInfo
7 |
8 |
9 | AGENDA_INFO_RAW = dict(status="defined", since=1, starttime=2, expiretime=3)
10 | # Parsed values are the same as the raw ones.
11 | AGENDA_INFO_PARSED = AGENDA_INFO_RAW
12 | AGENDA_INFO_ATTRS = (
13 | "status",
14 | "since",
15 | "startTime",
16 | "expireTime",
17 | )
18 |
19 |
20 | def test_agenda_info():
21 | do_test(
22 | AgendaInfo, AGENDA_INFO_RAW, AGENDA_INFO_PARSED, AGENDA_INFO_ATTRS,
23 | )
24 |
25 |
26 | AGENDA_CHOICES_RAW = dict(
27 | id="choices_id",
28 | description="description",
29 | bits=0,
30 | isabstain=False,
31 | isno=False,
32 | count=0,
33 | progress=0.0,
34 | )
35 | # Parsed values are the same as the raw ones.
36 | AGENDA_CHOICES_PARSED = AGENDA_CHOICES_RAW
37 | AGENDA_CHOICES_ATTRS = (
38 | "id",
39 | "description",
40 | "bits",
41 | "isAbstain",
42 | "isNo",
43 | "count",
44 | "progress",
45 | )
46 |
47 |
48 | def test_agenda_choices():
49 | do_test(
50 | AgendaChoices, AGENDA_CHOICES_RAW, AGENDA_CHOICES_PARSED, AGENDA_CHOICES_ATTRS,
51 | )
52 |
53 |
54 | AGENDA_RAW = dict(
55 | id="agenda_id",
56 | description="description",
57 | mask=0,
58 | starttime=0,
59 | expiretime=0,
60 | status="status",
61 | quorumprogress=0.0,
62 | choices=[AGENDA_CHOICES_RAW],
63 | )
64 | # Make a copy of the raw dict in order not to overwrite it.
65 | AGENDA_PARSED = dict(AGENDA_RAW)
66 | AGENDA_PARSED["choices"] = [AgendaChoices.parse(AGENDA_CHOICES_RAW)]
67 | AGENDA_ATTRS = (
68 | "id",
69 | "description",
70 | "mask",
71 | "startTime",
72 | "expireTime",
73 | "status",
74 | "quorumProgress",
75 | "choices",
76 | )
77 |
78 |
79 | def test_agenda():
80 | do_test(
81 | Agenda, AGENDA_RAW, AGENDA_PARSED, AGENDA_ATTRS,
82 | )
83 |
84 |
85 | AGENDAS_INFO_RAW = {
86 | "currentheight": 0,
87 | "startheight": 0,
88 | "endheight": 0,
89 | "hash": "hash",
90 | "voteversion": 0,
91 | "quorum": 0.0,
92 | "totalvotes": 0,
93 | "agendas": [AGENDA_RAW],
94 | }
95 | # Make a copy of the raw dict in order not to overwrite it.
96 | AGENDAS_INFO_PARSED = dict(AGENDAS_INFO_RAW)
97 | AGENDAS_INFO_PARSED["agendas"] = [Agenda.parse(AGENDA_RAW)]
98 | AGENDAS_INFO_ATTRS = (
99 | "currentHeight",
100 | "startHeight",
101 | "endHeight",
102 | "hash",
103 | "voteVersion",
104 | "quorum",
105 | "totalVotes",
106 | "agendas",
107 | )
108 |
109 |
110 | def test_agendas_info():
111 | do_test(
112 | AgendasInfo, AGENDAS_INFO_RAW, AGENDAS_INFO_PARSED, AGENDAS_INFO_ATTRS,
113 | )
114 |
115 |
116 | def do_test(class_, raw, parsed, attrs):
117 | """
118 | Iterate over the attributes defined in _ATTRS and make sure that
119 | the values generated by its "parse" method are the same as the ones in
120 | _PARSED.
121 |
122 | Separate _ATTRS tuples are needed because the attribute names are
123 | different from the keys of the dicts the values are gotten from.
124 | """
125 | obj = class_.parse(raw)
126 | for attr in attrs:
127 | assert getattr(obj, attr) == parsed[attr.lower()]
128 |
129 |
130 | def test_eq():
131 | # AgendaChoices
132 | choices = AgendaChoices.parse(AGENDA_CHOICES_RAW)
133 | assert choices != object()
134 | assert choices == choices
135 |
136 | # Agenda
137 | agenda = Agenda.parse(AGENDA_RAW)
138 | assert agenda != object()
139 | assert agenda == agenda
140 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/test_blockchain_unit.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred.crypto import crypto
9 | from decred.dcr import rpc
10 | from decred.dcr.blockchain import LocalNode
11 | from decred.dcr.nets import mainnet
12 | from decred.util.encode import ByteArray
13 |
14 |
15 | @pytest.fixture
16 | def node(monkeypatch, tmp_path):
17 | existsAddresses = set()
18 |
19 | class TestWsClient:
20 |
21 | closed = False
22 |
23 | def __init__(self, url, user, pw, cert=None):
24 | pass
25 |
26 | def existsAddresses(self, addrs):
27 | return [addr in existsAddresses for addr in addrs]
28 |
29 | monkeypatch.setattr(rpc, "WebsocketClient", TestWsClient)
30 | bc = LocalNode(
31 | netParams=mainnet,
32 | dbPath=tmp_path / "tmp.db",
33 | url="url",
34 | user="user",
35 | pw="pass",
36 | )
37 | assert bc.connected()
38 | bc._existsAddresses = existsAddresses
39 | return bc
40 |
41 |
42 | def newExtendedKey():
43 | xk = crypto.ExtendedKey.new(
44 | ByteArray("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef").b
45 | )
46 | return xk.deriveCoinTypeKey(mainnet)
47 |
48 |
49 | # A sampling of addresses derived from the newExtendedKey key.
50 | testAddrs = {
51 | 3: "DsapZcX3rBfrx85nbWLVoL5PpQAFUJW8ygV",
52 | 4: "Dscb7StES7pLCgWUVA6jBVpPMdHyKjqSVc4",
53 | 9: "DsTTd9nYHsE6ygQSYkeCF3Lt9x3gfnzJNNh",
54 | }
55 |
56 |
57 | def test_discoverAddresses(node):
58 | existAddrs = node._existsAddresses
59 |
60 | dcrKey = newExtendedKey()
61 |
62 | # Calling with index 0 and no addresses should return an empty result set.
63 | discovered = node.discoverAddresses(dcrKey, 0, 5)
64 | assert len(discovered) == 0
65 |
66 | # Stick an address at indexes 3 and 4
67 | existAddrs.add(testAddrs[3])
68 | existAddrs.add(testAddrs[4])
69 | discovered = node.discoverAddresses(dcrKey, 0, 5)
70 | assert len(discovered) == 2
71 | assert discovered[0] == 3
72 | assert discovered[1] == 4
73 |
74 | # Now stick one at 4 + gap = 9
75 | existAddrs.add(testAddrs[9])
76 | discovered = node.discoverAddresses(dcrKey, 0, 5)
77 | assert len(discovered) == 3
78 | assert discovered[2] == 9
79 |
80 | # But sticking in just the one at 9 should return nothing still
81 | existAddrs.clear()
82 | existAddrs.add(testAddrs[9])
83 | discovered = node.discoverAddresses(dcrKey, 0, 5)
84 | assert len(discovered) == 0
85 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/test_nets.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.dcr import nets
10 |
11 |
12 | def test_nets():
13 | assert nets.parse("mainnet") is nets.mainnet
14 |
15 | with pytest.raises(DecredError):
16 | nets.parse("nonet")
17 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/test_rpc_unit.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 |
5 | Tests use the "http_get_post" and "MockWebSocketClient" fixtures in conftest.py
6 | """
7 |
8 | import pytest
9 |
10 | from decred import DecredError
11 | from decred.dcr import rpc
12 | from decred.util import ws
13 | from decred.util.encode import ByteArray
14 |
15 |
16 | def test_Client(http_get_post):
17 | http_get_post(
18 | (
19 | "https://example.org",
20 | "{'jsonrpc': '2.0', 'id': 0, 'method': 'stop', 'params': ()}",
21 | ),
22 | '{"id": 0, "result": "dcrd stopping.", "error": ""}',
23 | )
24 | client = rpc.Client(url="https://example.org", user="username", pw="password",)
25 | assert client.stop() == "dcrd stopping."
26 |
27 |
28 | GET_ADDED_NODE_INFO_RESULT_ADDR_RAW = dict(address="127.0.0.1", connected="false")
29 |
30 |
31 | def test_GetAddedNodeInfoResultAddr():
32 | # Parsed values are the same as the raw ones.
33 | GET_ADDED_NODE_INFO_RESULT_ADDR_PARSED = GET_ADDED_NODE_INFO_RESULT_ADDR_RAW
34 | GET_ADDED_NODE_INFO_RESULT_ADDR_ATTRS = ("address", "connected")
35 |
36 | do_test(
37 | class_=rpc.GetAddedNodeInfoResultAddr,
38 | raw=GET_ADDED_NODE_INFO_RESULT_ADDR_RAW,
39 | parsed=GET_ADDED_NODE_INFO_RESULT_ADDR_PARSED,
40 | attrs=GET_ADDED_NODE_INFO_RESULT_ADDR_ATTRS,
41 | )
42 |
43 |
44 | def test_GetAddedNodeInfoResult():
45 | GET_ADDED_NODE_INFO_RESULT_RAW = dict(
46 | addednode="127.0.0.1",
47 | connected=False,
48 | addresses=[GET_ADDED_NODE_INFO_RESULT_ADDR_RAW],
49 | )
50 | # Make a copy of the raw dict in order not to overwrite it.
51 | GET_ADDED_NODE_INFO_RESULT_PARSED = dict(GET_ADDED_NODE_INFO_RESULT_RAW)
52 | GET_ADDED_NODE_INFO_RESULT_PARSED["addresses"] = [
53 | rpc.GetAddedNodeInfoResultAddr.parse(GET_ADDED_NODE_INFO_RESULT_ADDR_RAW)
54 | ]
55 | GET_ADDED_NODE_INFO_RESULT_ATTRS = ("addedNode", "connected", "addresses")
56 |
57 | do_test(
58 | class_=rpc.GetAddedNodeInfoResult,
59 | raw=GET_ADDED_NODE_INFO_RESULT_RAW,
60 | parsed=GET_ADDED_NODE_INFO_RESULT_PARSED,
61 | attrs=GET_ADDED_NODE_INFO_RESULT_ATTRS,
62 | )
63 |
64 |
65 | def test_GetWorkResult():
66 | GET_WORK_RESULT_RAW = dict(data=[0, 1], target=[2, 3])
67 | GET_WORK_RESULT_PARSED = dict(data=ByteArray([0, 1]), target=ByteArray([2, 3]))
68 | GET_WORK_RESULT_ATTRS = ("data", "target")
69 |
70 | do_test(
71 | class_=rpc.GetWorkResult,
72 | raw=GET_WORK_RESULT_RAW,
73 | parsed=GET_WORK_RESULT_PARSED,
74 | attrs=GET_WORK_RESULT_ATTRS,
75 | )
76 |
77 |
78 | def test_PrevOut():
79 | PREV_OUT_RAW = dict(value=1.0, addresses=["addr"])
80 | # Parsed values are the same as the raw ones.
81 | PREV_OUT_PARSED = PREV_OUT_RAW
82 | PREV_OUT_ATTRS = ("value", "addresses")
83 |
84 | do_test(
85 | class_=rpc.PrevOut,
86 | raw=PREV_OUT_RAW,
87 | parsed=PREV_OUT_PARSED,
88 | attrs=PREV_OUT_ATTRS,
89 | )
90 |
91 |
92 | def do_test(class_, raw, parsed, attrs):
93 | """
94 | Iterate over the attributes defined in _ATTRS and make sure that
95 | the values generated by its "parse" method are the same as the ones in
96 | _PARSED.
97 |
98 | Separate _ATTRS tuples are needed because the attribute names are
99 | different from the keys of the dicts the values are gotten from.
100 | """
101 | obj = class_.parse(raw)
102 | for attr in attrs:
103 | assert getattr(obj, attr) == parsed[attr.lower()]
104 |
105 |
106 | def test_eq():
107 | address = rpc.GetAddedNodeInfoResultAddr.parse(GET_ADDED_NODE_INFO_RESULT_ADDR_RAW)
108 | assert address != object()
109 | assert address == address
110 |
111 |
112 | def test_WebsocketClient(monkeypatch, MockWebSocketClient):
113 |
114 | monkeypatch.setattr(ws, "Client", MockWebSocketClient)
115 |
116 | wsClient = rpc.WebsocketClient(
117 | url="https://example.org", user="username", pw="password",
118 | )
119 | wsClient.requestTimeout = 1
120 |
121 | msg = '{"id": -1, "result": "", "error": "error"}'
122 | assert wsClient.on_message(msg) is None
123 |
124 | msg = "not_json"
125 | assert wsClient.on_message(msg) is None
126 |
127 | assert wsClient.call("no_reply") is None
128 |
129 | def send_with_reply(msg):
130 | wsClient.ws.on_message('{"id": 1, "result": "", "error": "error"}')
131 |
132 | # The response is sent back right away by the above class.
133 | wsClient.ws.send = send_with_reply
134 | with pytest.raises(DecredError):
135 | wsClient.call("no_such_method")
136 |
137 | assert wsClient.on_close(wsClient.ws) is None
138 |
139 | assert wsClient.on_error("error") is None
140 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/wire/test_msgblock.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | from decred.dcr.wire import msgblock
7 | from decred.util.encode import ByteArray
8 |
9 |
10 | class TestBlockHeader:
11 | def make_block_header(self):
12 | bh = msgblock.BlockHeader()
13 | bh.version = 6
14 | bh.prevBlock = reversed(
15 | ByteArray(
16 | "5cd0f1367afac81a6371785ad09dd67358b56257e6fc9e39f8f69be90855d20b"
17 | )
18 | )
19 | bh.merkleRoot = reversed(
20 | ByteArray(
21 | "da4fb4c83681ab9e7a186a9ccdf51e031a8319d57d8fbc5c4105b94050a77e97"
22 | )
23 | )
24 | bh.stakeRoot = reversed(
25 | ByteArray(
26 | "0000000000000000000000000000000000000000000000000000000000000000"
27 | )
28 | )
29 | bh.voteBits = 1
30 | bh.finalState = ByteArray("000000000000")
31 | bh.voters = 0
32 | bh.freshStake = 0
33 | bh.revocations = 0
34 | bh.poolSize = 0
35 | bh.bits = 545259519
36 | bh.sBits = 20000
37 | bh.height = 27
38 | bh.size = 358
39 | bh.timestamp = 1560468906
40 | bh.nonce = 0
41 | bh.extraData = ByteArray(
42 | "255221163779dfe8000000000000000000000000000000000000000000000000"
43 | )
44 | bh.stakeVersion = 0
45 | encoded = ByteArray(
46 | "060000000bd25508e99bf6f8399efce65762b55873d69dd05a7871631ac8fa7a36f1d05c"
47 | "977ea75040b905415cbc8f7dd519831a031ef5cd9c6a187a9eab8136c8b44fda00000000"
48 | "000000000000000000000000000000000000000000000000000000000100000000000000"
49 | "0000000000000000ffff7f20204e0000000000001b00000066010000aadd025d00000000"
50 | "255221163779dfe800000000000000000000000000000000000000000000000000000000"
51 | )
52 | return bh, encoded
53 |
54 | def test_decode(self):
55 | bh, encoded = self.make_block_header()
56 | b = bh.serialize()
57 | assert b == encoded
58 | reBH = msgblock.BlockHeader.unblob(ByteArray.hex(b))
59 | assert bh.version == reBH.version
60 | assert bh.prevBlock == reBH.prevBlock
61 | assert bh.merkleRoot == reBH.merkleRoot
62 | assert bh.stakeRoot == reBH.stakeRoot
63 | assert bh.voteBits == reBH.voteBits
64 | assert bh.finalState == reBH.finalState
65 | assert bh.voters == reBH.voters
66 | assert bh.freshStake == reBH.freshStake
67 | assert bh.revocations == reBH.revocations
68 | assert bh.poolSize == reBH.poolSize
69 | assert bh.bits == reBH.bits
70 | assert bh.sBits == reBH.sBits
71 | assert bh.height == reBH.height
72 | assert bh.size == reBH.size
73 | assert bh.timestamp == reBH.timestamp
74 | assert bh.nonce == reBH.nonce
75 | assert bh.extraData == reBH.extraData
76 | assert bh.stakeVersion == reBH.stakeVersion
77 | assert bh.id() == reBH.id()
78 |
79 | def test_cached_hash(self):
80 | bh, _ = self.make_block_header()
81 | assert bh.cachedH is None
82 | hash_ = bh.cachedHash()
83 | assert hash_ == bh.cachedHash()
84 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/wire/test_msgping.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | Based on dcrd MsgPing.
6 | """
7 |
8 | import random
9 |
10 | from decred.dcr.wire import wire
11 | from decred.dcr.wire.msgping import MsgPing
12 | from decred.util.encode import ByteArray
13 |
14 |
15 | def test_Ping():
16 | """
17 | Test the MsgPing API against the latest protocol version.
18 | """
19 | pver = wire.ProtocolVersion
20 |
21 | # Ensure we get the same nonce back out.
22 | nonce = random.randint(0, 0xFFFFFFFFFFFFFFFF)
23 | msg = MsgPing(nonce=nonce)
24 | assert msg.nonce == nonce
25 |
26 | # Ensure the command is expected value.
27 | assert msg.command() == "ping"
28 |
29 | maxPayload = msg.maxPayloadLength(pver)
30 | # Ensure max payload is expected value for latest protocol version.
31 | assert maxPayload == 8
32 |
33 | # Ensure max payload length is not more than MaxMessagePayload.
34 | assert maxPayload <= wire.MaxMessagePayload
35 |
36 |
37 | def test_PingWire():
38 | """
39 | Test the MsgPing wire encode and decode for various protocol versions.
40 | """
41 | nonce = 0x1E0F3
42 | msg = MsgPing(nonce)
43 | msgEncoded = ByteArray("f3e0010000000000")
44 |
45 | # Encode the message to wire format.
46 | b = msg.btcEncode(wire.ProtocolVersion)
47 | assert b == msgEncoded
48 |
49 | # Decode the message from wire format.
50 | reMsg = MsgPing.btcDecode(b, wire.ProtocolVersion)
51 | assert reMsg.nonce == nonce
52 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/wire/test_msgpong.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 |
5 | Based on dcrd MsgPing.
6 | """
7 |
8 | import random
9 |
10 | from decred.dcr.wire import wire
11 | from decred.dcr.wire.msgpong import MsgPong
12 | from decred.util.encode import ByteArray
13 |
14 |
15 | def test_Pong():
16 | """
17 | Test the MsgPong API against the latest protocol version.
18 | """
19 | pver = wire.ProtocolVersion
20 |
21 | nonce = random.randint(0, 0xFFFFFFFFFFFFFFFF)
22 | msg = MsgPong(nonce)
23 | assert msg.nonce == nonce
24 |
25 | # Ensure the command is expected value.
26 | assert msg.command() == "pong"
27 |
28 | # Ensure max payload is expected value for latest protocol version.
29 | maxPayload = msg.maxPayloadLength(pver)
30 | assert maxPayload == 8
31 |
32 | # Ensure max payload length is not more than MaxMessagePayload.
33 | assert maxPayload <= wire.MaxMessagePayload
34 |
35 | # Test encode with latest protocol version.
36 | b = msg.btcEncode(pver)
37 |
38 | # Test decode with latest protocol version.
39 | reMsg = MsgPong.btcDecode(b, pver)
40 |
41 | # Ensure nonce is the same.
42 | assert msg.nonce == reMsg.nonce
43 |
44 |
45 | def test_PongWire():
46 | """
47 | Test the MsgPong wire encode and decode for various protocol versions.
48 | """
49 | nonce = 0x1E0F3
50 | msg = MsgPong(nonce)
51 | msgEncoded = ByteArray("f3e0010000000000")
52 |
53 | # Encode the message to wire format.
54 | b = msg.btcEncode(wire.ProtocolVersion)
55 | assert b == msgEncoded
56 |
57 | # Decode the message from wire format.
58 | reMsg = MsgPong.btcDecode(b, wire.ProtocolVersion)
59 | assert reMsg.nonce == nonce
60 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/wire/test_msgverack.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, The Decred developers
3 | See LICENSE for details.
4 | """
5 |
6 | from decred.dcr.wire import wire
7 | from decred.dcr.wire.msgverack import MsgVerAck
8 | from decred.util.encode import ByteArray
9 |
10 |
11 | def test_VerAck():
12 | """ Test the MsgVerAck API. """
13 | pver = wire.ProtocolVersion
14 |
15 | # Ensure the command is expected value.
16 | msg = MsgVerAck()
17 | assert msg.command() == "verack"
18 |
19 | # Ensure max payload is expected value.
20 | maxPayload = msg.maxPayloadLength(pver)
21 | assert msg.maxPayloadLength(pver) == 0
22 |
23 | # Ensure max payload length is not more than MaxMessagePayload.
24 | assert maxPayload <= wire.MaxMessagePayload
25 |
26 |
27 | def test_VerAckWire():
28 | """
29 | Test the MsgVerAck wire encode and decode for various protocol versions.
30 | """
31 | msgVerAck = MsgVerAck()
32 | msgVerAckEncoded = ByteArray()
33 |
34 | # Encode the message to wire format.
35 | b = msgVerAck.btcEncode(wire.ProtocolVersion)
36 | assert b == msgVerAckEncoded
37 |
38 | # Decode the message from wire format. Just looking for exceptions.
39 | MsgVerAck.btcDecode(msgVerAckEncoded, wire.ProtocolVersion)
40 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/wire/test_netaddress.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.dcr.wire import netaddress, wire
10 | from decred.util.encode import ByteArray
11 |
12 |
13 | byteIP4 = bytes([127, 0, 0, 1])
14 |
15 |
16 | def test_NetAddress():
17 | ip = "127.0.0.1"
18 | port = 8333
19 |
20 | # Test NewNetAddress.
21 | na = netaddress.NetAddress(ip, port, 0)
22 |
23 | # Ensure we get the same ip, port, and services back out.
24 | assert byteIP4 == na.ip
25 | assert port == na.port
26 | assert na.services == 0
27 | assert not na.hasService(wire.SFNodeNetwork)
28 |
29 | # Ensure adding the full service node flag works.
30 | na.addService(wire.SFNodeNetwork)
31 | assert na.services == wire.SFNodeNetwork
32 | assert na.hasService(wire.SFNodeNetwork)
33 |
34 | # Ensure max payload is expected value for latest protocol version.
35 | wantPayload = 30
36 | maxPayload = netaddress.MaxNetAddressPayload
37 | assert maxPayload == wantPayload
38 |
39 | # Ensure max payload length is not more than MaxMessagePayload.
40 | assert maxPayload <= wire.MaxMessagePayload
41 |
42 |
43 | def test_NetAddressWire():
44 | """
45 | test the NetAddress wire encode and decode for various protocol versions and
46 | timestamp flag combinations.
47 | """
48 | # baseNetAddr is used in the various tests as a baseline NetAddress.
49 | baseNetAddr = netaddress.NetAddress(
50 | ip="127.0.0.1",
51 | port=8333,
52 | services=wire.SFNodeNetwork,
53 | stamp=0x495FAB29, # 2009-01-03 12:15:05 -0600 CST
54 | )
55 |
56 | # baseNetAddrNoTS is baseNetAddr with a zero value for the timestamp.
57 | baseNetAddrNoTS = netaddress.NetAddress(
58 | ip=baseNetAddr.ip,
59 | port=baseNetAddr.port,
60 | services=baseNetAddr.services,
61 | stamp=0,
62 | )
63 |
64 | # fmt: off
65 | # baseNetAddrEncoded is the wire encoded bytes of baseNetAddr.
66 | baseNetAddrEncoded = ByteArray([
67 | 0x29, 0xab, 0x5f, 0x49, # Timestamp
68 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # SFNodeNetwork
69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70 | 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, # IP 127.0.0.1
71 | 0x20, 0x8d, # Port 8333 in big-endian
72 | ])
73 |
74 | # baseNetAddrNoTSEncoded is the wire encoded bytes of baseNetAddrNoTS.
75 | baseNetAddrNoTSEncoded = ByteArray([
76 | # No timestamp
77 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, # SFNodeNetwork
78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 | 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01, # IP 127.0.0.1
80 | 0x20, 0x8d, # Port 8333 in big-endian
81 | ])
82 | # fmt: on
83 |
84 | """
85 | addrIn NetAddress to encode
86 | out Expected decoded NetAddress
87 | ts Include timestamp?
88 | buf Wire encoding
89 | pver Protoc
90 | """
91 |
92 | tests = [
93 | # Latest protocol version without ts flag.
94 | dict(
95 | addrIn=baseNetAddr,
96 | out=baseNetAddrNoTS,
97 | ts=False,
98 | buf=baseNetAddrNoTSEncoded,
99 | pver=wire.ProtocolVersion,
100 | ),
101 | # Latest protocol version with ts flag.
102 | dict(
103 | addrIn=baseNetAddr,
104 | out=baseNetAddr,
105 | ts=True,
106 | buf=baseNetAddrEncoded,
107 | pver=wire.ProtocolVersion,
108 | ),
109 | ]
110 |
111 | for test in tests:
112 | # Encode to wire format.
113 | b = netaddress.writeNetAddress(test["addrIn"], test["ts"])
114 | assert b == test["buf"]
115 |
116 | # Decode the message from wire format.
117 | na = netaddress.readNetAddress(test["buf"], test["ts"])
118 | assert byteIP4 == test["out"].ip
119 | assert na.port == test["out"].port
120 | assert na.services == test["out"].services
121 | assert na.timestamp == test["out"].timestamp
122 |
123 | baseNetAddr.ip = None
124 | b = netaddress.writeNetAddress(baseNetAddr, True)
125 | reNA = netaddress.readNetAddress(b, True)
126 | assert reNA.ip == ByteArray(length=16)
127 |
128 | # make sure a ipv6 address parses without an exception.
129 | netaddress.NetAddress(
130 | ip="2001:0db8:85a3:0000:0000:8a2e:0370:7334",
131 | port=8333,
132 | services=wire.SFNodeNetwork,
133 | stamp=0x495FAB29, # 2009-01-03 12:15:05 -0600 CST
134 | )
135 |
136 |
137 | def test_NetAddressDecodeErrors():
138 | """
139 | Peform negative tests against wire encode and decode NetAddress to confirm
140 | error paths work correctly.
141 | """
142 |
143 | # baseNetAddr is used in the various tests as a baseline NetAddress.
144 | baseNetAddr = netaddress.NetAddress(
145 | ip="127.0.0.1",
146 | port=8333,
147 | services=wire.SFNodeNetwork,
148 | stamp=0x495FAB29, # 2009-01-03 12:15:05 -0600 CST
149 | )
150 |
151 | bufWithTime = netaddress.writeNetAddress(baseNetAddr, True)
152 | bufWithoutTime = netaddress.writeNetAddress(baseNetAddr, False)
153 |
154 | tests = [
155 | (bufWithTime[:-1], True),
156 | (bufWithTime + [0], True),
157 | (bufWithoutTime[:-1], False),
158 | (bufWithoutTime + [0], False),
159 | (ByteArray(), True),
160 | ]
161 |
162 | for buf, hasStamp in tests:
163 | with pytest.raises(DecredError):
164 | netaddress.readNetAddress(buf, hasStamp)
165 |
--------------------------------------------------------------------------------
/decred/tests/unit/dcr/wire/test_wire.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.dcr.wire import wire
10 | from decred.util.encode import ByteArray
11 |
12 |
13 | class TestWire:
14 | # fmt: off
15 | data = (
16 | (0xFC, [0xFC]),
17 | (0xFD, [0xFD, 0xFD, 0x0]),
18 | (wire.MaxUint16, [0xFD, 0xFF, 0xFF]),
19 | (wire.MaxUint16 + 1, [0xFE, 0x0, 0x0, 0x1, 0x0]),
20 | (wire.MaxUint32, [0xFE, 0xFF, 0xFF, 0xFF, 0xFF]),
21 | (wire.MaxUint32 + 1, [0xFF, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0]),
22 | (wire.MaxUint64, [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]),
23 | )
24 | # fmt: on
25 |
26 | def test_write_var_int(self, prepareLogger):
27 | for val, bytes_ in self.data:
28 | from_val = wire.writeVarInt(wire.ProtocolVersion, val)
29 | from_bytes = ByteArray(bytes_)
30 | assert from_val == from_bytes
31 | val_from_bytes = wire.readVarInt(from_bytes, wire.ProtocolVersion)
32 | assert val_from_bytes == val
33 | with pytest.raises(DecredError):
34 | wire.writeVarInt(wire.ProtocolVersion, wire.MaxUint64 + 1)
35 |
36 | def test_read_var_int(self, prepareLogger):
37 | assert wire.readVarInt(ByteArray([0xFC]), wire.ProtocolVersion) == 0xFC
38 | with pytest.raises(DecredError):
39 | wire.readVarInt(
40 | ByteArray([0xFE, 0xFF, 0xFF, 0x0, 0x0]), wire.ProtocolVersion
41 | )
42 | with pytest.raises(DecredError):
43 | wire.readVarInt(ByteArray([0xFD, 0xFC, 0x0]), wire.ProtocolVersion)
44 |
--------------------------------------------------------------------------------
/decred/tests/unit/util/test_chains.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.util import chains
10 |
11 |
12 | def test_parseCoinType():
13 | with pytest.raises(DecredError):
14 | chains.parseCoinType("not_a_coin")
15 |
16 | assert chains.parseCoinType("DCR") == 42
17 | assert chains.parseCoinType(42) == 42
18 | assert chains.parseCoinType(-1) == -1
19 |
20 | with pytest.raises(DecredError):
21 | chains.parseCoinType(None)
22 |
--------------------------------------------------------------------------------
/decred/tests/unit/util/test_database.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import random
7 |
8 | import pytest
9 |
10 | from decred import DecredError
11 | from decred.util import database
12 | from decred.util.encode import ByteArray
13 |
14 |
15 | class TBlobber:
16 | def __init__(self, b):
17 | self.b = b
18 |
19 | @staticmethod
20 | def unblob(b):
21 | return TBlobber(ByteArray(b))
22 |
23 | @staticmethod
24 | def blob(tb):
25 | """Satisfies the encode.Blobber API"""
26 | return tb.b.bytes()
27 |
28 | def __eq__(self, other):
29 | return self.b == other.b
30 |
31 |
32 | def test_database(prepareLogger, randBytes):
33 | # Open a key value db in the temp directory.
34 | master = database.KeyValueDatabase(":memory:")
35 |
36 | # '$' in bucket name is illegal.
37 | with pytest.raises(DecredError):
38 | master.child("a$b")
39 |
40 | try:
41 | db = master.child("test")
42 |
43 | # Again, '$' in bucket name is illegal.
44 | with pytest.raises(DecredError):
45 | db.child("c$d")
46 |
47 | # Create some test data.
48 | random.seed(0)
49 | testPairs = [(randBytes(low=1), randBytes()) for _ in range(20)]
50 | runPairs(db, testPairs)
51 |
52 | # check integer keys and child naming scheme
53 | intdb = db.child("inttest", datatypes=("INTEGER", "BLOB"))
54 | assert intdb.name == "test$inttest"
55 | intdb[5] = b"asdf"
56 | assert intdb[5] == b"asdf"
57 | intdb[6] = b"jkl;"
58 | assert intdb.first() == (5, b"asdf")
59 | assert intdb.last() == (6, b"jkl;")
60 |
61 | # check uniqueness of keys:
62 | k = testPairs[0][0]
63 | db[k] = b"some new bytes"
64 | assert len([key for key in db if key == k]) == 1
65 |
66 | # test a serializable object
67 | randBlobber = lambda: TBlobber(ByteArray(randBytes()))
68 | objDB = master.child("blobber", blobber=TBlobber, unique=False)
69 | testPairs = [(randBytes(low=1), randBlobber()) for _ in range(20)]
70 | runPairs(objDB, testPairs)
71 |
72 | # non-uniqueness of keys
73 | k = testPairs[0][0]
74 | objDB[k] = randBlobber()
75 | assert len([key for key in objDB if key == k]) == 2
76 |
77 | # test a second-level child
78 | kidDB = db.child("kid")
79 | testPairs = [(randBytes(low=1), randBytes()) for _ in range(20)]
80 | runPairs(kidDB, testPairs)
81 |
82 | # uniqueness of table keys
83 | k = testPairs[0][0]
84 | kidDB[k] = b"some new bytes"
85 | assert len([key for key in kidDB if key == k]) == 1
86 |
87 | # slice notation
88 | sliceDB = db.child("slice", datatypes=("INTEGER", "TEXT"))
89 | n = 5
90 | for i in range(n):
91 | sliceDB[i] = str(i)
92 | nums = sliceDB[:n]
93 | assert len(nums) == 5
94 | assert all(i == int(s) for i, s in nums)
95 | assert sliceDB.last()[0] == 4
96 |
97 | finally:
98 | master.close()
99 |
100 |
101 | def runPairs(db, testPairs):
102 | ogKeys = {k: v for k, v in testPairs}
103 | values = [v for _, v in testPairs]
104 |
105 | # Ensure the db has zero length.
106 | assert len(db) == 0
107 |
108 | # Insert the test pairs.
109 | for k, v in testPairs:
110 | db[k] = v
111 |
112 | # Check length again
113 | assert len(db) == len(testPairs)
114 |
115 | # Check items iteration
116 | for k, v in db.items():
117 | assert k in ogKeys
118 | assert v == ogKeys[k]
119 |
120 | # Check key iteration.
121 | for k in db:
122 | assert k in ogKeys
123 | del ogKeys[k]
124 | assert len(ogKeys) == 0
125 |
126 | # Check value iteration.
127 | for v in db.values():
128 | values.remove(v)
129 | assert len(values) == 0
130 |
131 | # Delete an item
132 | k = testPairs[0][0]
133 | del db[k]
134 | # Check the length again
135 | assert len(db) == len(testPairs) - 1
136 |
137 | # Make sure the right row was deleted.
138 | with pytest.raises(database.NoValueError):
139 | v = db[k]
140 |
141 | # Remmove the corresponding test pair from the dict.
142 | testPairs.pop(0)
143 |
144 | # Make sure the rest are retrievable.
145 | for k, _ in testPairs:
146 | v = db[k]
147 |
148 | # Delete the rest
149 | for k, v in testPairs:
150 | del db[k]
151 |
152 | # Check the length
153 | assert len(db) == 0
154 |
155 | # Insert again
156 | for k, v in testPairs:
157 | db[k] = v
158 |
159 | # Make sure nothing has changed.
160 | for k, _ in testPairs:
161 | assert k in db
162 |
163 | # Clear the database, batch insert, and try again.
164 | db.clear()
165 | assert len(db) == 0
166 | db.batchInsert(testPairs)
167 | for k, _ in testPairs:
168 | assert k in db
169 |
--------------------------------------------------------------------------------
/decred/tests/unit/util/test_encode.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.util.encode import (
10 | BuildyBytes,
11 | ByteArray,
12 | decodeBlob,
13 | extractPushes,
14 | unblobCheck,
15 | )
16 |
17 |
18 | def test_ByteArray():
19 | makeA = lambda: ByteArray([0, 0, 255])
20 | makeB = lambda: ByteArray([0, 255, 0])
21 | makeC = lambda: ByteArray([255, 0, 0])
22 | zero = ByteArray([0, 0, 0])
23 |
24 | a = makeA()
25 | b = makeB()
26 | a |= b
27 | assert a == bytearray([0, 255, 255])
28 |
29 | c = makeC()
30 | a &= c
31 | assert a == zero
32 |
33 | a = makeA()
34 | a.zero()
35 | assert a == zero
36 |
37 | c = makeA()
38 | c |= 0
39 | assert a == zero
40 |
41 | a = makeA()
42 | c = makeC()
43 | assert a & c == zero
44 |
45 | assert makeA().iseven() is False
46 | assert makeB().iseven()
47 | assert makeC().iseven()
48 | assert zero.iseven()
49 |
50 | zero2 = ByteArray(zero)
51 | assert zero.b is not zero2.b
52 | assert zero == zero2
53 |
54 | zero2 = ByteArray(zero, copy=False)
55 | assert zero.b is zero2.b
56 |
57 | a = makeA()
58 | a |= makeC()
59 | a |= 65280
60 | assert a == bytearray([255, 255, 255])
61 | assert a != makeB()
62 | assert a != None # noqa
63 |
64 | assert makeA() < makeB()
65 | assert makeC() > makeB()
66 | assert makeA() != makeB()
67 | assert not (makeA() == None) # noqa
68 | assert makeA() != None # noqa
69 | assert makeA() <= makeA()
70 | assert makeB() >= makeA()
71 |
72 | a = makeA()
73 | a2 = ByteArray(zero)
74 | a2 |= a[2:]
75 | assert a is not a2
76 | assert a == a2
77 | assert a[2] == 255
78 |
79 | z = ByteArray(zero)
80 | z[2] = 255
81 | assert makeA() == z
82 |
83 | # encode.Blobber API
84 | assert isinstance(ByteArray.unblob(zero), ByteArray)
85 | assert ByteArray.blob(zero) == zero.b
86 |
87 | with pytest.raises(DecredError):
88 | zero[3] = 0
89 |
90 |
91 | def test_BuildyBytes():
92 | assert BuildyBytes().hex() == ""
93 |
94 | d0 = ByteArray([0x01, 0x02])
95 | d1 = ByteArray([0x03, 0x04, 0x05])
96 | d2 = ByteArray(b"")
97 | res = BuildyBytes(0).addData(d0).addData(d1).addData(d2)
98 | exp = ByteArray([0x00, 0x02, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x00])
99 | assert res == exp
100 |
101 | # Now do a versioned blob with a data push > 255 bytes.
102 | dBig = ByteArray(b"", length=257)
103 | dBig[100] = 0x12
104 | res = BuildyBytes(5).addData(d0).addData(d1).addData(d2).addData(dBig)
105 | ver, pushes = decodeBlob(res)
106 | assert ver == 5
107 | assert d0 == pushes[0]
108 | assert d1 == pushes[1]
109 | assert d2 == pushes[2]
110 | assert dBig == pushes[3]
111 |
112 | dHuge = ByteArray(b"", length=65536)
113 | with pytest.raises(DecredError):
114 | BuildyBytes(0).addData(dHuge)
115 |
116 |
117 | def test_decodeBlob():
118 | with pytest.raises(DecredError):
119 | decodeBlob(b"")
120 | assert decodeBlob(bytes((0, 0x01, 0x02))) == (0, [b"\x02"])
121 |
122 |
123 | def test_extractPushes():
124 | assert len(extractPushes(b"")) == 0
125 |
126 | with pytest.raises(DecredError):
127 | extractPushes(ByteArray([0xFF]))
128 |
129 | assert extractPushes(ByteArray([0xFF, 0x00, 0x00])) == [ByteArray()]
130 |
131 | with pytest.raises(DecredError):
132 | extractPushes(ByteArray([0x01]))
133 |
134 | assert extractPushes(ByteArray([0x00])) == [ByteArray()]
135 | assert extractPushes(ByteArray([0x01, 0x00])) == [ByteArray([0x00])]
136 |
137 |
138 | def test_unblobCheck():
139 | data = {0: 1}
140 | # Unsupported version.
141 | with pytest.raises(NotImplementedError):
142 | unblobCheck("test", 1, 0, data)
143 | # Unexpected pushes.
144 | with pytest.raises(DecredError):
145 | unblobCheck("test", 0, 2, data)
146 | # No errors.
147 | assert unblobCheck("test", 0, 1, data) is None
148 |
--------------------------------------------------------------------------------
/decred/tests/unit/util/test_tinyhttp.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import urllib.request as urlrequest
7 |
8 | import pytest
9 |
10 | from decred import DecredError
11 | from decred.util import tinyhttp
12 |
13 |
14 | @pytest.fixture
15 | def urlopen(monkeypatch):
16 | """
17 | Tests will use the returned "queue" function to add responses they want
18 | returned from "urllib.request.urlopen" when used in tinyhttp.
19 | """
20 | q = {}
21 |
22 | def mock_urlopen(req, context=None):
23 | """
24 | Args:
25 | req: an object with "full_url" and "data" attributes, for instance
26 | urllib.request.Request.
27 | Return:
28 | an object with "read" and "decode" methods, for instance
29 | http.client.HTTPResponse.
30 | """
31 |
32 | class Response(str):
33 | read = lambda self: self
34 | decode = lambda self: self
35 |
36 | key = (req.full_url, req.data)
37 | print(req.data)
38 | reply = q[key].pop()
39 | return Response(reply)
40 |
41 | monkeypatch.setattr(urlrequest, "urlopen", mock_urlopen)
42 |
43 | def queue(url, data=None, reply=""):
44 | q.setdefault((url, data), []).append(reply)
45 |
46 | return queue
47 |
48 |
49 | def test_get(urlopen):
50 | urlopen("http://example.org/", None, "[0, 1, 2]")
51 | assert tinyhttp.get("http://example.org/") == [0, 1, 2]
52 |
53 |
54 | def test_post(urlopen):
55 | urlopen("http://example.org/", b'{"a": "b"}', '{"c": "d"}')
56 | assert tinyhttp.post("http://example.org/", {"a": "b"}) == {"c": "d"}
57 |
58 |
59 | def test_request_urlencoded(urlopen):
60 | urlopen("http://example.org/", b"a=b", '{"c": "d"}')
61 | assert tinyhttp.request(
62 | "http://example.org/", postData={"a": "b"}, urlEncode=True,
63 | ) == {"c": "d"}
64 |
65 |
66 | def test_request_urlopen_error(monkeypatch):
67 | def urlopen_error(req, context=None):
68 | raise DecredError("test error")
69 |
70 | monkeypatch.setattr(urlrequest, "urlopen", urlopen_error)
71 | with pytest.raises(DecredError):
72 | tinyhttp.get("http://example.org/")
73 |
74 |
75 | def test_request_json_decode_error(urlopen):
76 | urlopen("http://example.org/", b'{"a": "b"}', "nojson")
77 | assert tinyhttp.request("http://example.org/", postData={"a": "b"}) == "nojson"
78 |
--------------------------------------------------------------------------------
/decred/tests/unit/wallet/test_accounts.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019-2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import pytest
7 |
8 | from decred import DecredError
9 | from decred.crypto import crypto, rando
10 | from decred.dcr import account, addrlib, nets
11 | from decred.util import chains, database, encode
12 | from decred.wallet import accounts
13 |
14 |
15 | testSeed = encode.ByteArray(
16 | "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
17 | ).b
18 |
19 | tRoot = crypto.ExtendedKey.new(testSeed)
20 |
21 |
22 | def test_child_neuter(prepareLogger):
23 | """
24 | Test the ExtendedKey.neuter method.
25 | """
26 | extKey = crypto.ExtendedKey.new(testSeed)
27 | extKey.setNetwork(nets.mainnet)
28 | extKey.child(0)
29 | pub = extKey.neuter()
30 | expStr = (
31 | "dpubZ9169KDAEUnyo8vdTJcpFWeaUEKH3G6detaXv46HxtQcENwxGBbRqbfTCJ9BUnWP"
32 | + "CkE8WApKPJ4h7EAapnXCZq1a9AqWWzs1n31VdfwbrQk"
33 | )
34 | assert pub.string() == expStr
35 |
36 |
37 | def test_change_addresses(prepareLogger):
38 | """
39 | Test internal branch address derivation.
40 | """
41 | cryptoKey = rando.newKey()
42 | db = database.KeyValueDatabase(":memory:").child("tmp")
43 | # ticker for coin type is ok. Case insensitive.
44 | acctMgr = accounts.createNewAccountManager(
45 | tRoot, cryptoKey, "DcR", nets.mainnet, db
46 | )
47 | acct = acctMgr.openAccount(0, cryptoKey)
48 | for i in range(10):
49 | acct.nextInternalAddress()
50 |
51 |
52 | def test_account_manager(monkeypatch, prepareLogger):
53 | # Set up globals for test.
54 | monkeypatch.setattr(account, "DefaultGapLimit", 2)
55 |
56 | cryptoKey = rando.newKey()
57 | db = database.KeyValueDatabase(":memory:").child("tmp")
58 | # 42 = Decred
59 | acctMgr = accounts.createNewAccountManager(tRoot, cryptoKey, 42, nets.mainnet, db)
60 |
61 | acct = acctMgr.openAccount(0, cryptoKey)
62 | tempAcct = acctMgr.addAccount(cryptoKey, "temp")
63 | assert acctMgr.account(1) == tempAcct
64 | assert acctMgr.listAccounts() == [acct, tempAcct]
65 |
66 | acctMgr.setNode("node")
67 | assert acctMgr.node == "node"
68 | assert tempAcct.node == "node"
69 |
70 | acctMgr.accounts[3] = tempAcct
71 | del acctMgr.accounts[1]
72 | with pytest.raises(DecredError):
73 | acctMgr.listAccounts()
74 | del acctMgr.accounts[3]
75 |
76 | with pytest.raises(DecredError):
77 | accounts.AccountManager.unblob(encode.BuildyBytes(0))
78 |
79 | zeroth = acct.currentAddress()
80 | b = acctMgr.serialize()
81 | reAM = accounts.AccountManager.unblob(b.b)
82 | assert acctMgr.coinType == reAM.coinType
83 | assert acctMgr.netName == reAM.netName
84 | assert acctMgr.netName == reAM.netName
85 |
86 | reAM.load(db, None)
87 | reAcct = reAM.openAccount(0, cryptoKey)
88 | reZeroth = reAcct.currentAddress()
89 | assert zeroth == reZeroth
90 |
91 | acctMgr.saveAccount(0)
92 | db = acctMgr.dbForAcctIdx(0)
93 | assert db.name == "tmp$accts$0"
94 |
95 |
96 | def test_discover(monkeypatch, prepareLogger):
97 | cryptoKey = rando.newKey()
98 | db = database.KeyValueDatabase(":memory:").child("tmp")
99 | acctMgr = accounts.createNewAccountManager(
100 | tRoot, cryptoKey, "dcr", nets.mainnet, db
101 | )
102 | txs = {}
103 |
104 | class Blockchain:
105 | params = nets.mainnet
106 | addrsHaveTxs = lambda addrs: any(a in txs for a in addrs)
107 |
108 | # Set up globals for test.
109 | origChain = chains.chain("dcr")
110 | chains.registerChain("dcr", Blockchain)
111 | # Set up globals for test.
112 | monkeypatch.setattr(accounts, "ACCOUNT_GAP_LIMIT", 2)
113 | monkeypatch.setattr(account, "DefaultGapLimit", 4)
114 |
115 | acctMgr.discover(cryptoKey)
116 | assert len(acctMgr.accounts) == 1
117 |
118 | coinExtKey = acctMgr.coinKey(cryptoKey)
119 | acct2ExtKey = coinExtKey.deriveAccountKey(2).neuter().child(0)
120 | acct2Addr3 = addrlib.deriveChildAddress(acct2ExtKey, 3, nets.mainnet)
121 | txs[acct2Addr3] = ["tx"]
122 | acctMgr.discover(cryptoKey)
123 | assert len(acctMgr.accounts) == 3
124 |
125 | # Restore globals.
126 | chains.registerChain("dcr", origChain)
127 |
--------------------------------------------------------------------------------
/tinywallet/.coveragerc:
--------------------------------------------------------------------------------
1 | # Test coverage tool configuration, see reference at
2 | # https://coverage.readthedocs.io/en/latest/config.html
3 |
4 | [run]
5 | omit = tests/*
6 | dynamic_context = test_function
7 |
8 | [report]
9 | # Regexes for lines to exclude from consideration
10 | exclude_lines =
11 | nocover
12 |
13 | # Don't complain about debug-only code.
14 | def __repr__
15 |
16 | # Don't complain if non-runnable code isn't run.
17 | if __name__ == .__main__.:
18 |
19 | # Don't complain if tests don't hit defensive assertion code.
20 | raise AssertionError
21 | raise NotImplementedError
22 | raise CrazyKeyError
23 | raise ParameterRangeError
24 |
25 | ignore_errors = True
26 | # skip_covered = True
27 | skip_empty = True
28 |
29 | [html]
30 | show_contexts = True
31 |
--------------------------------------------------------------------------------
/tinywallet/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | count = True
3 | max-complexity = 10
4 | max-line-length = 88
5 | statistics = True
6 | exclude =
7 | .git,
8 | .venv,
9 | .pytest_cache,
10 | __pycache__
11 |
--------------------------------------------------------------------------------
/tinywallet/README.md:
--------------------------------------------------------------------------------
1 | # TinyWallet
2 |
3 | TinyWallet is a light [Decred](https://decred.org/) wallet GUI application
4 | based on PyQt5.
5 |
6 | **The light wallet is experimental, and should not be used on mainnet.**
7 |
8 | To start the wallet install the `tinywallet` package from the Python Package
9 | Index and run the `tinywallet` command.
10 |
11 | The wallet runs as a system-tray application, of which the major difference is
12 | that "closing" the wallet actually just removes the entry from the taskbar and
13 | minimizes the window "to the system tray".
14 | The wallet can then be "opened" again through the icon in the system tray.
15 |
16 | ![alt text][screenshot]
17 |
18 | TinyWallet is pretty small.
19 | Like Decred, it's meant to be an omnipresent yet largely invisible and
20 | unobtrusive part of your digital environment.
21 | The small dialog size keeps user interactions focused.
22 | Bells and whistles are minimized in favor of simplicity whenever possible.
23 | Blockchain mechanics are invisible.
24 | The goal is to make using Decred easier than pulling change out of your pocket.
25 |
26 | [screenshot]:
27 | https://user-images.githubusercontent.com/6109680/62095772-08b4ce80-b247-11e9-81ae-66931ebb07be.png
28 |
--------------------------------------------------------------------------------
/tinywallet/coverage-html.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ## To generate line-by-line test coverage viewable in a web browser,
3 | ## change to this directory, then install the dependencies:
4 | # poetry install
5 | ## The coverage report will be in the ./htmlcov/ directory.
6 | poetry run pytest --cov-report=html --cov=tinywallet ./tests/
7 |
--------------------------------------------------------------------------------
/tinywallet/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "tinywallet"
3 | version = "0.0.1"
4 | description = "A Python 3 Decred wallet."
5 | license = "ISC"
6 | homepage = "https://decred.org/"
7 | repository = "https://github.com/decred/tinydecred/"
8 | documentation = "https://github.com/decred/tinydecred/blob/master/tinywallet"
9 | authors = [
10 | "Brian Stafford ",
11 | "The Decred developers "
12 | ]
13 | classifiers = [
14 | "Development Status :: 3 - Alpha",
15 | "Environment :: X11 Applications :: Qt",
16 | "Intended Audience :: End Users/Desktop",
17 | "License :: OSI Approved :: ISC License (ISCL)",
18 | "Natural Language :: English",
19 | "Operating System :: OS Independent",
20 | "Programming Language :: Python :: 3.6",
21 | "Programming Language :: Python :: 3.7",
22 | "Programming Language :: Python :: 3.8",
23 | "Topic :: Office/Business :: Financial"
24 | ]
25 |
26 | [tool.poetry.dependencies]
27 | python = "^3.6"
28 | decred = {path = "../decred/"}
29 | pyqt5 = "=5.12.3"
30 |
31 | [tool.poetry.dev-dependencies]
32 | pytest = "^5.3.5"
33 | pytest-cov = "^2.8.1"
34 | flake8 = "^3.7.9"
35 | black = "^19.10b0"
36 | isort = "^4.3.21"
37 | Cython = "^0.29.16"
38 |
39 | [tool.poetry.scripts]
40 | tinywallet = "tinywallet.app:main"
41 |
42 | [tool.isort]
43 | atomic = "true"
44 | combine_as_imports = "true"
45 | combine_star = "true"
46 | filter_files = "true"
47 | force_grid_wrap = 0
48 | force_sort_within_sections = "true"
49 | include_trailing_comma = "true"
50 | line_length = 88
51 | lines_after_imports = 2
52 | multi_line_output = 3
53 | use_parentheses = "true"
54 | virtual_env = "./.venv/"
55 |
56 | [build-system]
57 | requires = ["poetry>=1.0.3"]
58 | build-backend = "poetry.masonry.api"
59 |
--------------------------------------------------------------------------------
/tinywallet/tests/unit/test_config.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2020, the Decred developers
3 | See LICENSE for details
4 | """
5 |
6 | import logging
7 | import sys
8 |
9 | import pytest
10 |
11 | from decred.dcr.nets import DcrdPorts, simnet
12 | from decred.util import helpers
13 | from tinywallet import ui # noqa, for coverage.
14 | from tinywallet.config import CmdArgs, dcrd, load
15 |
16 |
17 | def test_CmdArgs():
18 | sys.argv = ["cmd", "--unknown"]
19 | with pytest.raises(SystemExit):
20 | cfg = CmdArgs()
21 |
22 | sys.argv = ["cmd", "--simnet"]
23 | cfg = CmdArgs()
24 | assert cfg.netParams.Name == "simnet"
25 |
26 | sys.argv = ["cmd", "--testnet", "--loglevel", ",:"]
27 | with pytest.raises(SystemExit):
28 | cfg = CmdArgs()
29 |
30 | sys.argv = ["cmd", "--testnet", "--loglevel", "debug"]
31 | cfg = CmdArgs()
32 | assert cfg.netParams.Name == "testnet3"
33 | assert cfg.logLevel == logging.DEBUG
34 |
35 | sys.argv = ["cmd", "--loglevel", "A:Warning,B:deBug,C:Critical,D:0"]
36 | cfg = CmdArgs()
37 | assert len(cfg.moduleLevels) == 4
38 | assert cfg.moduleLevels["A"] == logging.WARNING
39 | assert cfg.moduleLevels["B"] == logging.DEBUG
40 | assert cfg.moduleLevels["C"] == logging.CRITICAL
41 | assert cfg.moduleLevels["D"] == logging.NOTSET
42 |
43 |
44 | def test_dcrd(monkeypatch, tmpdir):
45 | def mock_appDataDir(app_name):
46 | return tmpdir
47 |
48 | monkeypatch.setattr(helpers, "appDataDir", mock_appDataDir)
49 | assert dcrd(simnet) == {}
50 |
51 | cfg_file = tmpdir.join("dcrd.conf")
52 |
53 | cfg_file.write("")
54 | assert dcrd(simnet) is None
55 |
56 | cfg_file.write("rpcuser=username\n")
57 | cfg = dcrd(simnet)
58 | assert "rpc.cert" in cfg["rpccert"]
59 | assert cfg["rpclisten"] == f"localhost:{DcrdPorts[simnet.Name]}"
60 | assert not cfg["notls"]
61 |
62 | tests = [
63 | ("rpclisten=", f"localhost:{DcrdPorts[simnet.Name]}"),
64 | ("rpclisten=0.0.0.0", f"0.0.0.0:{DcrdPorts[simnet.Name]}"),
65 | ("rpclisten=::", f"localhost:{DcrdPorts[simnet.Name]}"),
66 | ("rpclisten=:9109", "localhost:9109"),
67 | ("rpclisten=0.0.0.0:9109", "0.0.0.0:9109"),
68 | ("rpclisten=[::]:9109", "localhost:9109"),
69 | ("rpclisten=[::1]:9109", "localhost:9109"),
70 | ("rpclisten=:8337\nrpclisten=:9999", "localhost:9999"),
71 | ]
72 | for rpclistenCfg, want in tests:
73 | cfg_file.write(f"rpcuser=username\n{rpclistenCfg}\n")
74 | assert dcrd(simnet)["rpclisten"] == want
75 |
76 | tests = [
77 | ("notls=1", True),
78 | ("notls=0", False),
79 | ]
80 | for notlsCfg, want in tests:
81 | cfg_file.write(f"rpcuser=username\n{notlsCfg}\n")
82 | assert dcrd(simnet)["notls"] == want
83 |
84 |
85 | def test_load():
86 | assert isinstance(load(), CmdArgs)
87 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/__init__.py
--------------------------------------------------------------------------------
/tinywallet/tinywallet/config.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | Copyright (c) 2019-2020, the Decred developers
4 | See LICENSE for details
5 |
6 | Configuration settings for TinyDecred.
7 | """
8 |
9 | import argparse
10 | import logging
11 | import os
12 | import sys
13 | from urllib.parse import urlparse
14 |
15 | from appdirs import AppDirs
16 |
17 | from decred.dcr import nets
18 | from decred.util import helpers
19 |
20 |
21 | # Set the data directory in a OS-appropriate location.
22 | _ad = AppDirs("TinyWallet", False)
23 | DATA_DIR = _ad.user_data_dir
24 |
25 | helpers.mkdir(DATA_DIR)
26 |
27 | # The master configuration file name.
28 | CONFIG_NAME = "tinywallet.conf"
29 | CONFIG_PATH = os.path.join(DATA_DIR, CONFIG_NAME)
30 |
31 | # Some decred constants.
32 | MAINNET = nets.mainnet.Name
33 | TESTNET = nets.testnet.Name
34 | SIMNET = nets.simnet.Name
35 |
36 | logLevelMap = {
37 | "critical": logging.CRITICAL,
38 | "error": logging.ERROR,
39 | "warning": logging.WARNING,
40 | "debug": logging.DEBUG,
41 | "notset": logging.NOTSET,
42 | "0": logging.NOTSET,
43 | }
44 |
45 |
46 | def logLvl(s):
47 | """
48 | Get the log level from the map.
49 |
50 | Args:
51 | s (str): A string which is a key for the logLevelMap. Case-insensitive.
52 | """
53 | return logLevelMap[s.lower()]
54 |
55 |
56 | # Some network specific default settings.
57 | NetworkDefaults = {
58 | MAINNET: {"dcrdata": "https://explorer.dcrdata.org/"},
59 | TESTNET: {"dcrdata": "https://testnet.dcrdata.org/"},
60 | SIMNET: {"dcrdata": "http://localhost:17779"},
61 | }
62 |
63 |
64 | class CmdArgs:
65 | """
66 | CmdArgs are command-line configuration options.
67 | """
68 |
69 | def __init__(self):
70 | self.logLevel = logging.INFO
71 | self.moduleLevels = {}
72 | parser = argparse.ArgumentParser()
73 | parser.add_argument("--loglevel")
74 | netGroup = parser.add_mutually_exclusive_group()
75 | netGroup.add_argument("--simnet", action="store_true", help="use simnet")
76 | netGroup.add_argument("--testnet", action="store_true", help="use testnet")
77 | args, unknown = parser.parse_known_args()
78 | if unknown:
79 | sys.exit(f"unknown arguments: {unknown}")
80 | self.netParams = nets.mainnet
81 | if args.simnet:
82 | self.netParams = nets.simnet
83 | elif args.testnet:
84 | self.netParams = nets.testnet
85 | else:
86 | print("**********************************************************")
87 | print(" WARNING. WALLET FOR TESTING ONLY. NOT FOR USE ON MAINNET ")
88 | print("**********************************************************")
89 | if args.loglevel:
90 | try:
91 | if any(ch in args.loglevel for ch in (",", ":")):
92 | pairs = (s.split(":") for s in args.loglevel.split(","))
93 | self.moduleLevels = {k: logLvl(v) for k, v in pairs}
94 | else:
95 | self.logLevel = logLvl(args.loglevel)
96 | except Exception:
97 | sys.exit(f"malformed loglevel specifier: {args.loglevel}")
98 |
99 |
100 | class DB:
101 | """Various database keys used by tinywallet modules"""
102 |
103 | wallet = "wallet".encode()
104 | theme = "theme".encode()
105 | dcrdata = "dcrdata".encode()
106 | rpcuser = "rpcuser".encode()
107 | rpcpass = "rpcpass".encode()
108 | rpccert = "rpccert".encode()
109 | rpchost = "rpchost".encode()
110 | dcrdon = "dcrdon".encode()
111 |
112 |
113 | def dcrd(netParams):
114 | """
115 | Attempt to fetch the dcrd configuration settings. Values will be parsed for
116 | 'notls', 'rpcuser', 'rpcpass', 'rpclisten', and 'rpccert'. The setting for
117 | 'rpcuser' must be present in the file. If values are not found for 'rpccert'
118 | or 'rpclisten', default locations are populated.
119 |
120 | Args:
121 | netParams (bool): The network parameters.
122 |
123 | Returns:
124 | dict or None: The parsed configuration settings.
125 | """
126 | dcrdCfgDir = helpers.appDataDir("dcrd")
127 | cfgPath = os.path.join(dcrdCfgDir, "dcrd.conf")
128 | if not os.path.isfile(cfgPath):
129 | return {}
130 | dcrdCfg = helpers.readINI(
131 | cfgPath, ["notls", "rpclisten", "rpcuser", "rpcpass", "rpccert"]
132 | )
133 | if "rpcuser" not in dcrdCfg:
134 | return None
135 | if "rpccert" not in dcrdCfg:
136 | dcrdCfg["rpccert"] = os.path.join(dcrdCfgDir, "rpc.cert")
137 | rpcHost = "localhost"
138 | rpcPort = nets.DcrdPorts[netParams.Name]
139 | if "rpclisten" in dcrdCfg:
140 | try:
141 | rpcUrl = urlparse(f"//{dcrdCfg['rpclisten']}")
142 | if rpcUrl.hostname:
143 | rpcHost = rpcUrl.hostname.replace("::1", "").strip(":") or rpcHost
144 | if rpcUrl.port:
145 | rpcPort = rpcUrl.port
146 | except ValueError:
147 | pass
148 | dcrdCfg["rpclisten"] = f"{rpcHost}:{rpcPort}"
149 | dcrdCfg["notls"] = dcrdCfg["notls"] == "1" if "notls" in dcrdCfg else False
150 | return dcrdCfg
151 |
152 |
153 | tinyConfig = None
154 |
155 |
156 | def load():
157 | """
158 | Load and return the current command-line configuration. The configuration is
159 | only loaded once. Successive calls to the modular `load` function will
160 | return the same instance.
161 |
162 | Returns:
163 | CmdArgs: The current command-line configuration.
164 | """
165 | global tinyConfig
166 | if not tinyConfig:
167 | tinyConfig = CmdArgs()
168 | return tinyConfig
169 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-Bold.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-BoldItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-ExtraBold.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-Italic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-Medium.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-MediumItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-Regular.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-SemiBold.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/EBGaramond-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/EBGaramond-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/MaterialIcons-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/MaterialIcons-Regular.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-Bold.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-BoldItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-Italic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-Light.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-LightItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-Medium.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-MediumItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-Regular.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-Thin.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/fonts/RobotoMono-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/decred/tinydecred/f7f7d9f7da8d49d9ae9a72e5579b07a3b8572267/tinywallet/tinywallet/fonts/RobotoMono-ThinItalic.ttf
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
59 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/check.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
64 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/closed-eye.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
67 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/folder.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
67 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/gear.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/home.svg:
--------------------------------------------------------------------------------
1 |
2 |
36 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/locked.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/open-eye.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
69 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/send.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
64 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/spinner.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/unlocked.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/icons/x.svg:
--------------------------------------------------------------------------------
1 |
2 |
52 |
--------------------------------------------------------------------------------
/tinywallet/tinywallet/ui.py:
--------------------------------------------------------------------------------
1 | """
2 | Copyright (c) 2019, Brian Stafford
3 | Copyright (c) 2019, the Decred developers
4 | See LICENSE for details
5 | """
6 |
7 | import os
8 |
9 |
10 | PACKAGEDIR = os.path.dirname(os.path.realpath(__file__))
11 | FONTDIR = os.path.join(PACKAGEDIR, "fonts")
12 |
13 | TINY = "tiny"
14 | SMALL = "small"
15 | MEDIUM = "medium"
16 | LARGE = "large"
17 |
18 | BALANCE_SIGNAL = "balance_signal"
19 | SYNC_SIGNAL = "sync_signal"
20 | WORKING_SIGNAL = "working_signal"
21 | DONE_SIGNAL = "done_signal"
22 | PURCHASEINFO_SIGNAL = "purchaseinfo_signal"
23 | SPENT_TICKETS_SIGNAL = "spent_tickets_signal"
24 |
--------------------------------------------------------------------------------