mirror of
https://github.com/planetmint/planetmint.git
synced 2025-09-14 12:00:11 +00:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
975921183c | ||
![]() |
a848324e1d | ||
![]() |
58131d445a | ||
![]() |
f3077ee8e3 | ||
![]() |
ef00a7fdde | ||
![]() |
ce1649f7db | ||
![]() |
472d4cfbd9 | ||
![]() |
9279dd680b | ||
![]() |
1571211a24 | ||
![]() |
67abb7102d | ||
![]() |
3ac0ca2c69 | ||
![]() |
4bf1af6f06 | ||
![]() |
0d947a4083 | ||
![]() |
34e5492420 | ||
![]() |
4c55f576b9 | ||
![]() |
b2bca169ec | ||
![]() |
3e223f04cd | ||
![]() |
95001fc262 | ||
![]() |
923f14d669 | ||
![]() |
74d3c732b1 | ||
![]() |
5c4923dbd6 | ||
![]() |
884c3cc32b | ||
![]() |
4feeed5862 | ||
![]() |
461fae27d1 | ||
![]() |
033235fb16 | ||
![]() |
11cf86464f | ||
![]() |
9f4cc292bc | ||
![]() |
6a3c655e3b |
40
.github/workflows/CI.yml
vendored
40
.github/workflows/CI.yml
vendored
@ -41,11 +41,8 @@ jobs:
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Install pip-audit
|
||||
run: pip install --upgrade pip pip-audit
|
||||
|
||||
- name: Setup poetry
|
||||
uses: Gr1N/setup-poetry@v7
|
||||
uses: Gr1N/setup-poetry@v8
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install
|
||||
@ -54,7 +51,34 @@ jobs:
|
||||
run: poetry run pip freeze > requirements.txt
|
||||
|
||||
- name: Audit dependencies
|
||||
run: poetry run pip-audit --ignore-vuln PYSEC-2022-42969 --ignore-vuln PYSEC-2022-203 --ignore-vuln GHSA-r9hx-vwmv-q579
|
||||
run: |
|
||||
poetry run pip-audit \
|
||||
--ignore-vuln GHSA-8495-4g3g-x7pr \
|
||||
--ignore-vuln PYSEC-2024-230 \
|
||||
--ignore-vuln PYSEC-2024-225 \
|
||||
--ignore-vuln GHSA-3ww4-gg4f-jr7f \
|
||||
--ignore-vuln GHSA-9v9h-cgj8-h64p \
|
||||
--ignore-vuln GHSA-h4gh-qq45-vh27 \
|
||||
--ignore-vuln PYSEC-2023-62 \
|
||||
--ignore-vuln PYSEC-2024-71 \
|
||||
--ignore-vuln GHSA-84pr-m4jr-85g5 \
|
||||
--ignore-vuln GHSA-w3h3-4rj7-4ph4 \
|
||||
--ignore-vuln PYSEC-2024-60 \
|
||||
--ignore-vuln GHSA-h5c8-rqwp-cp95 \
|
||||
--ignore-vuln GHSA-h75v-3vvj-5mfj \
|
||||
--ignore-vuln GHSA-q2x7-8rv6-6q7h \
|
||||
--ignore-vuln GHSA-gmj6-6f8f-6699 \
|
||||
--ignore-vuln PYSEC-2023-117 \
|
||||
--ignore-vuln GHSA-m87m-mmvp-v9qm \
|
||||
--ignore-vuln GHSA-9wx4-h78v-vm56 \
|
||||
--ignore-vuln GHSA-34jh-p97f-mpxf \
|
||||
--ignore-vuln PYSEC-2022-203 \
|
||||
--ignore-vuln PYSEC-2023-58 \
|
||||
--ignore-vuln PYSEC-2023-57 \
|
||||
--ignore-vuln PYSEC-2023-221 \
|
||||
--ignore-vuln GHSA-2g68-c3qc-8985 \
|
||||
--ignore-vuln GHSA-f9vj-2wh5-fj8j \
|
||||
--ignore-vuln GHSA-q34m-jh98-gwm2
|
||||
|
||||
test:
|
||||
needs: lint
|
||||
@ -82,10 +106,10 @@ jobs:
|
||||
run: sudo apt-get update && sudo apt-get install -y git zsh curl tarantool-common vim build-essential cmake
|
||||
|
||||
- name: Get Tendermint
|
||||
run: wget https://github.com/tendermint/tendermint/releases/download/v0.34.15/tendermint_0.34.15_linux_amd64.tar.gz && tar zxf tendermint_0.34.15_linux_amd64.tar.gz
|
||||
run: wget https://github.com/tendermint/tendermint/releases/download/v0.34.24/tendermint_0.34.24_linux_amd64.tar.gz && tar zxf tendermint_0.34.24_linux_amd64.tar.gz
|
||||
|
||||
- name: Setup poetry
|
||||
uses: Gr1N/setup-poetry@v7
|
||||
uses: Gr1N/setup-poetry@v8
|
||||
|
||||
- name: Install Planetmint
|
||||
run: poetry install --with dev
|
||||
@ -108,7 +132,7 @@ jobs:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Setup poetry
|
||||
uses: Gr1N/setup-poetry@v7
|
||||
uses: Gr1N/setup-poetry@v8
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install --with dev
|
||||
|
37
.github/workflows/audit.yml
vendored
37
.github/workflows/audit.yml
vendored
@ -21,11 +21,8 @@ jobs:
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Install pip-audit
|
||||
run: pip install --upgrade pip
|
||||
|
||||
- name: Setup poetry
|
||||
uses: Gr1N/setup-poetry@v7
|
||||
uses: Gr1N/setup-poetry@v8
|
||||
|
||||
- name: Install dependencies
|
||||
run: poetry install
|
||||
@ -34,4 +31,34 @@ jobs:
|
||||
run: poetry run pip freeze > requirements.txt
|
||||
|
||||
- name: Audit dependencies
|
||||
run: poetry run pip-audit --ignore-vuln PYSEC-2022-42969 --ignore-vuln PYSEC-2022-203 --ignore-vuln GHSA-r9hx-vwmv-q579
|
||||
run: |
|
||||
poetry run pip-audit \
|
||||
--ignore-vuln PYSEC-2022-203 \
|
||||
--ignore-vuln PYSEC-2023-58 \
|
||||
--ignore-vuln PYSEC-2023-57 \
|
||||
--ignore-vuln PYSEC-2023-62 \
|
||||
--ignore-vuln GHSA-8495-4g3g-x7pr \
|
||||
--ignore-vuln PYSEC-2023-135 \
|
||||
--ignore-vuln PYSEC-2024-230 \
|
||||
--ignore-vuln PYSEC-2024-225 \
|
||||
--ignore-vuln GHSA-3ww4-gg4f-jr7f \
|
||||
--ignore-vuln GHSA-9v9h-cgj8-h64p \
|
||||
--ignore-vuln GHSA-h4gh-qq45-vh27 \
|
||||
--ignore-vuln PYSEC-2024-71 \
|
||||
--ignore-vuln GHSA-84pr-m4jr-85g5 \
|
||||
--ignore-vuln GHSA-w3h3-4rj7-4ph4 \
|
||||
--ignore-vuln PYSEC-2024-60 \
|
||||
--ignore-vuln GHSA-h5c8-rqwp-cp95 \
|
||||
--ignore-vuln GHSA-h75v-3vvj-5mfj \
|
||||
--ignore-vuln GHSA-q2x7-8rv6-6q7h \
|
||||
--ignore-vuln GHSA-gmj6-6f8f-6699 \
|
||||
--ignore-vuln PYSEC-2023-117 \
|
||||
--ignore-vuln GHSA-m87m-mmvp-v9qm \
|
||||
--ignore-vuln GHSA-9wx4-h78v-vm56 \
|
||||
--ignore-vuln PYSEC-2023-192 \
|
||||
--ignore-vuln PYSEC-2023-212 \
|
||||
--ignore-vuln GHSA-34jh-p97f-mpxf \
|
||||
--ignore-vuln PYSEC-2023-221 \
|
||||
--ignore-vuln GHSA-2g68-c3qc-8985 \
|
||||
--ignore-vuln GHSA-f9vj-2wh5-fj8j \
|
||||
--ignore-vuln GHSA-q34m-jh98-gwm2
|
||||
|
22
.github/workflows/sonar.yaml
vendored
22
.github/workflows/sonar.yaml
vendored
@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Sonar Scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Sonar Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
# Shallow clones should be disabled for a better relevancy of analysis
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: sonarsource/sonarqube-scan-action@master
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
31
CHANGELOG.md
31
CHANGELOG.md
@ -25,6 +25,37 @@ For reference, the possible headings are:
|
||||
* **Known Issues**
|
||||
* **Notes**
|
||||
|
||||
## [2.5.1] - 2023-22-06
|
||||
* **Fixed** docker image incompatibility with tarantool installer, switched to ubuntu-container for AIO image
|
||||
|
||||
## [2.5.0] - 2023-21-06
|
||||
* **Changed** Upgraded ABCI compatbility to Tendermint v0.34.24 and CometBFT v0.34.29
|
||||
|
||||
## [2.4.7] - 2023-24-05
|
||||
* **Fixed** wrong referencing of planetmint-transactions object and variable
|
||||
|
||||
## [2.4.6] - 2023-24-05
|
||||
* **Fixed** Missing ABCI_RPC object initiailization for CLI voting commands.
|
||||
* **Fixed** TypeError in EndBlock procedure that occured rarely within the network.
|
||||
* **Security** moved to a more secure requests version
|
||||
|
||||
## [2.4.5] - 2023-21-04
|
||||
* **Fixed** Integration of DataAccessor Singleton class to reduce potentially multiple DB driver initializations.
|
||||
|
||||
## [2.4.4] - 2023-19-04
|
||||
* **Fixed** tarantool migration script issues (modularity, script failures, cli cmd to function mapping)
|
||||
|
||||
## [2.4.3] - 2023-17-04
|
||||
* **Fixed** fixed migration behaviour for non docker service
|
||||
|
||||
## [2.4.2] - 2023-13-04
|
||||
* **Added** planetmint migration commands
|
||||
|
||||
## [2.4.1] - 2023-11-04
|
||||
* **Removed** Fastquery class
|
||||
* **Changed** UTXO space updated to resemble outputs
|
||||
* **Changed** updated UTXO querying
|
||||
|
||||
## [2.4.0] - 2023-29-03
|
||||
* **Added** Zenroom script validation
|
||||
* **Changed** adjusted zenroom testing for new transaction script structure
|
||||
|
@ -1,7 +1,7 @@
|
||||
FROM python:3.9-slim
|
||||
FROM ubuntu:22.04
|
||||
LABEL maintainer "contact@ipdb.global"
|
||||
|
||||
ARG TM_VERSION=0.34.15
|
||||
ARG TM_VERSION=0.34.24
|
||||
RUN mkdir -p /usr/src/app
|
||||
ENV HOME /root
|
||||
COPY . /usr/src/app/
|
||||
@ -11,15 +11,17 @@ RUN apt-get update \
|
||||
&& apt-get install -y openssl ca-certificates git \
|
||||
&& apt-get install -y vim build-essential cmake jq zsh wget \
|
||||
&& apt-get install -y libstdc++6 \
|
||||
&& apt-get install -y openssh-client openssh-server \
|
||||
&& pip install --upgrade pip cffi \
|
||||
&& apt-get install -y openssh-client openssh-server
|
||||
RUN apt-get install -y python3 python3-pip cython3
|
||||
RUN pip install --upgrade pip cffi \
|
||||
&& pip install -e . \
|
||||
&& apt-get autoremove
|
||||
|
||||
# Install tarantool and monit
|
||||
RUN apt-get install -y dirmngr gnupg apt-transport-https software-properties-common ca-certificates curl
|
||||
RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime
|
||||
RUN apt-get update
|
||||
RUN curl -L https://tarantool.io/wrATeGF/release/2/installer.sh | bash
|
||||
RUN curl -L https://tarantool.io/release/2/installer.sh | bash
|
||||
RUN apt-get install -y tarantool monit
|
||||
|
||||
# Install Tendermint
|
||||
@ -42,7 +44,7 @@ ENV PLANETMINT_WSSERVER_ADVERTISED_HOST 0.0.0.0
|
||||
ENV PLANETMINT_WSSERVER_ADVERTISED_SCHEME ws
|
||||
ENV PLANETMINT_TENDERMINT_PORT 26657
|
||||
|
||||
COPY planetmint/backend/tarantool/init.lua /etc/tarantool/instances.enabled
|
||||
COPY planetmint/backend/tarantool/opt/init.lua /etc/tarantool/instances.enabled
|
||||
|
||||
VOLUME /data/db /data/configdb /tendermint
|
||||
|
||||
|
6
Makefile
6
Makefile
@ -26,7 +26,7 @@ export PRINT_HELP_PYSCRIPT
|
||||
# Basic commands #
|
||||
##################
|
||||
DOCKER := docker
|
||||
DC := docker-compose
|
||||
DC := docker compose
|
||||
HELP := python -c "$$PRINT_HELP_PYSCRIPT"
|
||||
ECHO := /usr/bin/env echo
|
||||
|
||||
@ -65,8 +65,8 @@ test: check-deps test-unit ## Run unit
|
||||
|
||||
test-unit: check-deps ## Run all tests once or specify a file/test with TEST=tests/file.py::Class::test
|
||||
@$(DC) up -d tarantool
|
||||
#wget https://github.com/tendermint/tendermint/releases/download/v0.34.15/tendermint_0.34.15_linux_amd64.tar.gz
|
||||
#tar zxf tendermint_0.34.15_linux_amd64.tar.gz
|
||||
#wget https://github.com/tendermint/tendermint/releases/download/v0.34.24/tendermint_0.34.24_linux_amd64.tar.gz
|
||||
#tar zxf tendermint_0.34.24_linux_amd64.tar.gz
|
||||
poetry run pytest -m "not abci"
|
||||
rm -rf ~/.tendermint && ./tendermint init && ./tendermint node --consensus.create_empty_blocks=false --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://localhost:26658&
|
||||
poetry run pytest -m abci
|
||||
|
@ -1,3 +1,4 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
|
@ -120,11 +120,8 @@ def test_env_config(monkeypatch):
|
||||
assert result == expected
|
||||
|
||||
|
||||
@pytest.mark.skip
|
||||
def test_autoconfigure_read_both_from_file_and_env(
|
||||
monkeypatch, request
|
||||
): # TODO Disabled until we create a better config format
|
||||
return
|
||||
@pytest.mark.skip(reason="Disabled until we create a better config format")
|
||||
def test_autoconfigure_read_both_from_file_and_env(monkeypatch, request):
|
||||
# constants
|
||||
DATABASE_HOST = "test-host"
|
||||
DATABASE_NAME = "test-dbname"
|
||||
@ -210,7 +207,7 @@ def test_autoconfigure_read_both_from_file_and_env(
|
||||
"advertised_port": WSSERVER_ADVERTISED_PORT,
|
||||
},
|
||||
"database": database_mongodb,
|
||||
"tendermint": {"host": "localhost", "port": 26657, "version": "v0.34.15"},
|
||||
"tendermint": {"host": "localhost", "port": 26657, "version": "v0.34.24"},
|
||||
"log": {
|
||||
"file": LOG_FILE,
|
||||
"level_console": "debug",
|
||||
|
38
docker-compose-aio.yml
Normal file
38
docker-compose-aio.yml
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
version: '2.2'
|
||||
|
||||
services:
|
||||
planetmint-all-in-one:
|
||||
image: planetmint/planetmint-aio:latest
|
||||
expose:
|
||||
- "22"
|
||||
- "9984"
|
||||
- "9985"
|
||||
- "26656"
|
||||
- "26657"
|
||||
- "26658"
|
||||
command: ["/usr/src/app/scripts/pre-config-planetmint.sh", "/usr/src/app/scripts/all-in-one.bash"]
|
||||
volumes:
|
||||
- ./integration/scripts:/usr/src/app/scripts
|
||||
- shared:/shared
|
||||
scale: ${SCALE:-4}
|
||||
|
||||
test:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: integration/python/Dockerfile
|
||||
depends_on:
|
||||
- planetmint-all-in-one
|
||||
command: ["/scripts/pre-config-test.sh", "/scripts/wait-for-planetmint.sh", "/scripts/test.sh", "pytest", "/src"]
|
||||
environment:
|
||||
SCALE: ${SCALE:-4}
|
||||
volumes:
|
||||
- ./integration/python/src:/src
|
||||
- ./integration/scripts:/scripts
|
||||
- ./integration/cli:/tests
|
||||
- shared:/shared
|
@ -1,3 +1,4 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
@ -22,7 +23,7 @@ services:
|
||||
- "3303:3303"
|
||||
- "8081:8081"
|
||||
volumes:
|
||||
- ./planetmint/backend/tarantool/init.lua:/opt/tarantool/init.lua
|
||||
- ./planetmint/backend/tarantool/opt/init.lua:/opt/tarantool/init.lua
|
||||
entrypoint: tarantool /opt/tarantool/init.lua
|
||||
restart: always
|
||||
planetmint:
|
||||
@ -64,7 +65,7 @@ services:
|
||||
restart: always
|
||||
|
||||
tendermint:
|
||||
image: tendermint/tendermint:v0.34.15
|
||||
image: tendermint/tendermint:v0.34.24
|
||||
# volumes:
|
||||
# - ./tmdata:/tendermint
|
||||
entrypoint: ''
|
||||
|
@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
""" Script to build http examples for http server api docs """
|
||||
"""Script to build http examples for http server api docs"""
|
||||
|
||||
import json
|
||||
import os
|
||||
@ -189,7 +189,6 @@ def main():
|
||||
ctx["public_keys_transfer"] = tx_transfer.outputs[0].public_keys[0]
|
||||
ctx["tx_transfer_id"] = tx_transfer.id
|
||||
|
||||
# privkey_transfer_last = 'sG3jWDtdTXUidBJK53ucSTrosktG616U3tQHBk81eQe'
|
||||
pubkey_transfer_last = "3Af3fhhjU6d9WecEM9Uw5hfom9kNEwE7YuDWdqAUssqm"
|
||||
|
||||
cid = 0
|
||||
|
@ -198,7 +198,6 @@ todo_include_todos = False
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "press"
|
||||
# html_theme = 'sphinx_documatt_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
|
@ -30,9 +30,9 @@ The version of Planetmint Server described in these docs only works well with Te
|
||||
|
||||
```bash
|
||||
$ sudo apt install -y unzip
|
||||
$ wget https://github.com/tendermint/tendermint/releases/download/v0.34.15/tendermint_v0.34.15_linux_amd64.zip
|
||||
$ unzip tendermint_v0.34.15_linux_amd64.zip
|
||||
$ rm tendermint_v0.34.15_linux_amd64.zip
|
||||
$ wget https://github.com/tendermint/tendermint/releases/download/v0.34.24/tendermint_v0.34.24_linux_amd64.zip
|
||||
$ unzip tendermint_v0.34.24_linux_amd64.zip
|
||||
$ rm tendermint_v0.34.24_linux_amd64.zip
|
||||
$ sudo mv tendermint /usr/local/bin
|
||||
```
|
||||
|
||||
|
@ -59,8 +59,8 @@ $ sudo apt install mongodb
|
||||
```
|
||||
Tendermint can be installed and started as follows
|
||||
```
|
||||
$ wget https://github.com/tendermint/tendermint/releases/download/v0.34.15/tendermint_0.34.15_linux_amd64.tar.gz
|
||||
$ tar zxf tendermint_0.34.15_linux_amd64.tar.gz
|
||||
$ wget https://github.com/tendermint/tendermint/releases/download/v0.34.24/tendermint_0.34.24_linux_amd64.tar.gz
|
||||
$ tar zxf tendermint_0.34.24_linux_amd64.tar.gz
|
||||
$ ./tendermint init
|
||||
$ ./tendermint node --proxy_app=tcp://localhost:26658
|
||||
```
|
||||
|
@ -60,7 +60,7 @@ you can do this:
|
||||
.. code::
|
||||
|
||||
$ mkdir $(pwd)/tmdata
|
||||
$ docker run --rm -v $(pwd)/tmdata:/tendermint/config tendermint/tendermint:v0.34.15 init
|
||||
$ docker run --rm -v $(pwd)/tmdata:/tendermint/config tendermint/tendermint:v0.34.24 init
|
||||
$ cat $(pwd)/tmdata/genesis.json
|
||||
|
||||
You should see something that looks like:
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM tendermint/tendermint:v0.34.15
|
||||
FROM tendermint/tendermint:v0.34.24
|
||||
LABEL maintainer "contact@ipdb.global"
|
||||
WORKDIR /
|
||||
USER root
|
||||
|
@ -1,3 +1,4 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
|
@ -1,3 +1,4 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
|
@ -1,3 +1,4 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
|
@ -1,4 +1,4 @@
|
||||
ARG tm_version=v0.31.5
|
||||
ARG tm_version=v0.34.24
|
||||
FROM tendermint/tendermint:${tm_version}
|
||||
LABEL maintainer "contact@ipdb.global"
|
||||
WORKDIR /
|
||||
|
@ -1,3 +1,4 @@
|
||||
---
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
|
@ -17,7 +17,7 @@ stack_size=${STACK_SIZE:=4}
|
||||
stack_type=${STACK_TYPE:="docker"}
|
||||
stack_type_provider=${STACK_TYPE_PROVIDER:=""}
|
||||
# NOTE versions prior v0.28.0 have different priv_validator format!
|
||||
tm_version=${TM_VERSION:="v0.34.15"}
|
||||
tm_version=${TM_VERSION:="v0.34.24"}
|
||||
mongo_version=${MONGO_VERSION:="3.6"}
|
||||
stack_vm_memory=${STACK_VM_MEMORY:=2048}
|
||||
stack_vm_cpus=${STACK_VM_CPUS:=2}
|
||||
|
@ -16,7 +16,7 @@ stack_repo=${STACK_REPO:="planetmint/planetmint"}
|
||||
stack_size=${STACK_SIZE:=4}
|
||||
stack_type=${STACK_TYPE:="docker"}
|
||||
stack_type_provider=${STACK_TYPE_PROVIDER:=""}
|
||||
tm_version=${TM_VERSION:="0.31.5"}
|
||||
tm_version=${TM_VERSION:="0.34.24"}
|
||||
mongo_version=${MONGO_VERSION:="3.6"}
|
||||
stack_vm_memory=${STACK_VM_MEMORY:=2048}
|
||||
stack_vm_cpus=${STACK_VM_CPUS:=2}
|
||||
|
@ -81,9 +81,7 @@ class ApplicationLogic(BaseApplication):
|
||||
chain_id = known_chain["chain_id"]
|
||||
|
||||
if known_chain["is_synced"]:
|
||||
msg = (
|
||||
f"Got invalid InitChain ABCI request ({genesis}) - " f"the chain {chain_id} is already synced."
|
||||
)
|
||||
msg = f"Got invalid InitChain ABCI request ({genesis}) - the chain {chain_id} is already synced."
|
||||
logger.error(msg)
|
||||
sys.exit(1)
|
||||
if chain_id != genesis.chain_id:
|
||||
@ -238,8 +236,7 @@ class ApplicationLogic(BaseApplication):
|
||||
block_txn_hash = calculate_hash(self.block_txn_ids)
|
||||
block = self.validator.models.get_latest_block()
|
||||
|
||||
logger.debug("BLOCK: ", block)
|
||||
|
||||
logger.debug(f"BLOCK: {block}")
|
||||
if self.block_txn_ids:
|
||||
self.block_txn_hash = calculate_hash([block["app_hash"], block_txn_hash])
|
||||
else:
|
||||
@ -250,6 +247,8 @@ class ApplicationLogic(BaseApplication):
|
||||
sys.exit(1)
|
||||
except ValueError:
|
||||
sys.exit(1)
|
||||
except TypeError:
|
||||
sys.exit(1)
|
||||
|
||||
return ResponseEndBlock(validator_updates=validator_update)
|
||||
|
||||
@ -278,7 +277,7 @@ class ApplicationLogic(BaseApplication):
|
||||
sys.exit(1)
|
||||
|
||||
logger.debug(
|
||||
"Commit-ing new block with hash: apphash=%s ," "height=%s, txn ids=%s",
|
||||
"Commit-ing new block with hash: apphash=%s, height=%s, txn ids=%s",
|
||||
data,
|
||||
self.new_height,
|
||||
self.block_txn_ids,
|
||||
|
@ -79,7 +79,6 @@ def new_validator_set(validators, updates):
|
||||
|
||||
def get_public_key_decoder(pk):
|
||||
encoding = pk["type"]
|
||||
decoder = base64.b64decode
|
||||
|
||||
if encoding == "ed25519-base16":
|
||||
decoder = base64.b16decode
|
||||
|
@ -28,14 +28,14 @@ from planetmint.backend.models.output import Output
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
from planetmint.config import Config
|
||||
from planetmint.config_utils import load_validation_plugin
|
||||
from planetmint.utils.singleton import Singleton
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Validator:
|
||||
def __init__(self, async_io: bool = False):
|
||||
self.async_io = async_io
|
||||
self.models = DataAccessor(async_io=async_io)
|
||||
def __init__(self):
|
||||
self.models = DataAccessor()
|
||||
self.validation = Validator._get_validation_method()
|
||||
|
||||
@staticmethod
|
||||
@ -61,9 +61,7 @@ class Validator:
|
||||
if tx.operation != Transaction.COMPOSE:
|
||||
asset_id = tx.get_asset_id(input_txs)
|
||||
if asset_id != Transaction.read_out_asset_id(tx):
|
||||
raise AssetIdMismatch(
|
||||
("The asset id of the input does not" " match the asset id of the" " transaction")
|
||||
)
|
||||
raise AssetIdMismatch(("The asset id of the input does not match the asset id of the transaction"))
|
||||
else:
|
||||
asset_ids = Transaction.get_asset_ids(input_txs)
|
||||
if Transaction.read_out_asset_id(tx) in asset_ids:
|
||||
@ -105,9 +103,9 @@ class Validator:
|
||||
|
||||
if output_amount != input_amount:
|
||||
raise AmountError(
|
||||
(
|
||||
"The amount used in the inputs `{}`" " needs to be same as the amount used" " in the outputs `{}`"
|
||||
).format(input_amount, output_amount)
|
||||
"The amount used in the inputs `{}` needs to be same as the amount used in the outputs `{}`".format(
|
||||
input_amount, output_amount
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
@ -202,7 +200,7 @@ class Validator:
|
||||
raise InvalidProposer("Public key is not a part of the validator set")
|
||||
|
||||
# NOTE: Check if all validators have been assigned votes equal to their voting power
|
||||
if not self.is_same_topology(current_validators, transaction.outputs):
|
||||
if not Validator.is_same_topology(current_validators, transaction.outputs):
|
||||
raise UnequalValidatorSet("Validator set much be exactly same to the outputs of election")
|
||||
|
||||
if transaction.operation == VALIDATOR_ELECTION:
|
||||
@ -210,7 +208,8 @@ class Validator:
|
||||
|
||||
return transaction
|
||||
|
||||
def is_same_topology(cls, current_topology, election_topology):
|
||||
@staticmethod
|
||||
def is_same_topology(current_topology, election_topology):
|
||||
voters = {}
|
||||
for voter in election_topology:
|
||||
if len(voter.public_keys) > 1:
|
||||
@ -269,7 +268,7 @@ class Validator:
|
||||
value as the `voting_power`
|
||||
"""
|
||||
validators = {}
|
||||
for validator in self.models.get_validators(height):
|
||||
for validator in self.models.get_validators(height=height):
|
||||
# NOTE: we assume that Tendermint encodes public key in base64
|
||||
public_key = public_key_from_ed25519_key(key_from_base64(validator["public_key"]["value"]))
|
||||
validators[public_key] = validator["voting_power"]
|
||||
@ -493,7 +492,7 @@ class Validator:
|
||||
self.migrate_abci_chain()
|
||||
if election.operation == VALIDATOR_ELECTION:
|
||||
validator_updates = [election.assets[0].data]
|
||||
curr_validator_set = self.models.get_validators(new_height)
|
||||
curr_validator_set = self.models.get_validators(height=new_height)
|
||||
updated_validator_set = new_validator_set(curr_validator_set, validator_updates)
|
||||
|
||||
updated_validator_set = [v for v in updated_validator_set if v["voting_power"] > 0]
|
||||
|
@ -64,7 +64,6 @@ class DBConnection(metaclass=DBSingleton):
|
||||
backend: str = None,
|
||||
connection_timeout: int = None,
|
||||
max_tries: int = None,
|
||||
async_io: bool = False,
|
||||
**kwargs
|
||||
):
|
||||
"""Create a new :class:`~.Connection` instance.
|
||||
|
@ -73,7 +73,7 @@ class LocalMongoDBConnection(DBConnection):
|
||||
try:
|
||||
return query.run(self.connect())
|
||||
except pymongo.errors.AutoReconnect:
|
||||
logger.warning("Lost connection to the database, " "retrying query.")
|
||||
logger.warning("Lost connection to the database, retrying query.")
|
||||
return query.run(self.connect())
|
||||
except pymongo.errors.AutoReconnect as exc:
|
||||
raise ConnectionError from exc
|
||||
|
@ -77,7 +77,7 @@ def get_assets(conn, asset_ids):
|
||||
|
||||
|
||||
@register_query(LocalMongoDBConnection)
|
||||
def get_spent(conn, transaction_id, output):
|
||||
def get_spending_transaction(conn, transaction_id, output):
|
||||
query = {
|
||||
"inputs": {
|
||||
"$elemMatch": {"$and": [{"fulfills.transaction_id": transaction_id}, {"fulfills.output_index": output}]}
|
||||
@ -167,21 +167,6 @@ def delete_transactions(conn, txn_ids):
|
||||
conn.run(conn.collection("transactions").delete_many({"id": {"$in": txn_ids}}))
|
||||
|
||||
|
||||
@register_query(LocalMongoDBConnection)
|
||||
def store_unspent_outputs(conn, *unspent_outputs):
|
||||
if unspent_outputs:
|
||||
try:
|
||||
return conn.run(
|
||||
conn.collection("utxos").insert_many(
|
||||
unspent_outputs,
|
||||
ordered=False,
|
||||
)
|
||||
)
|
||||
except DuplicateKeyError:
|
||||
# TODO log warning at least
|
||||
pass
|
||||
|
||||
|
||||
@register_query(LocalMongoDBConnection)
|
||||
def delete_unspent_outputs(conn, *unspent_outputs):
|
||||
if unspent_outputs:
|
||||
|
@ -68,12 +68,14 @@ class Output:
|
||||
|
||||
@staticmethod
|
||||
def outputs_dict(output: dict, transaction_id: str = "") -> Output:
|
||||
out_dict: Output
|
||||
if output["condition"]["details"].get("subconditions") is None:
|
||||
out_dict = Output.output_with_public_key(output, transaction_id)
|
||||
else:
|
||||
out_dict = Output.output_with_sub_conditions(output, transaction_id)
|
||||
return out_dict
|
||||
return Output(
|
||||
transaction_id=transaction_id,
|
||||
public_keys=output["public_keys"],
|
||||
amount=output["amount"],
|
||||
condition=Condition(
|
||||
uri=output["condition"]["uri"], details=ConditionDetails.from_dict(output["condition"]["details"])
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def from_tuple(output: tuple) -> Output:
|
||||
@ -110,25 +112,3 @@ class Output:
|
||||
@staticmethod
|
||||
def list_to_dict(output_list: list[Output]) -> list[dict]:
|
||||
return [output.to_dict() for output in output_list or []]
|
||||
|
||||
@staticmethod
|
||||
def output_with_public_key(output, transaction_id) -> Output:
|
||||
return Output(
|
||||
transaction_id=transaction_id,
|
||||
public_keys=output["public_keys"],
|
||||
amount=output["amount"],
|
||||
condition=Condition(
|
||||
uri=output["condition"]["uri"], details=ConditionDetails.from_dict(output["condition"]["details"])
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def output_with_sub_conditions(output, transaction_id) -> Output:
|
||||
return Output(
|
||||
transaction_id=transaction_id,
|
||||
public_keys=output["public_keys"],
|
||||
amount=output["amount"],
|
||||
condition=Condition(
|
||||
uri=output["condition"]["uri"], details=ConditionDetails.from_dict(output["condition"]["details"])
|
||||
),
|
||||
)
|
||||
|
@ -133,7 +133,7 @@ def get_asset(connection, asset_id) -> Asset:
|
||||
|
||||
|
||||
@singledispatch
|
||||
def get_spent(connection, transaction_id, condition_id):
|
||||
def get_spending_transaction(connection, transaction_id, condition_id):
|
||||
"""Check if a `txid` was already used as an input.
|
||||
|
||||
A transaction can be used as an input for another transaction. Bigchain
|
||||
@ -208,7 +208,7 @@ def get_block_with_transaction(connection, txid):
|
||||
|
||||
|
||||
@singledispatch
|
||||
def store_transaction_outputs(connection, output: Output, index: int):
|
||||
def store_transaction_outputs(connection, output: Output, index: int, table: str):
|
||||
"""Store the transaction outputs.
|
||||
|
||||
Args:
|
||||
@ -264,13 +264,6 @@ def store_block(conn, block):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@singledispatch
|
||||
def store_unspent_outputs(connection, unspent_outputs):
|
||||
"""Store unspent outputs in ``utxo_set`` table."""
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@singledispatch
|
||||
def delete_unspent_outputs(connection, unspent_outputs):
|
||||
"""Delete unspent outputs in ``utxo_set`` table."""
|
||||
@ -455,6 +448,12 @@ def get_outputs_by_tx_id(connection, tx_id: str) -> list[Output]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@singledispatch
|
||||
def get_outputs_by_owner(connection, public_key: str, table: str) -> list[Output]:
|
||||
"""Retrieve an owners outputs by public key"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@singledispatch
|
||||
def get_metadata(conn, transaction_ids):
|
||||
"""Retrieve metadata for a list of transactions by their ids"""
|
||||
|
@ -137,6 +137,18 @@ def init_database(connection, dbname):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@singledispatch
|
||||
def migrate(connection):
|
||||
"""Migrate database
|
||||
|
||||
Args:
|
||||
connection (:class:`~planetmint.backend.connection.Connection`): an
|
||||
existing connection to use to migrate the database.
|
||||
Creates one if not given.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def validate_language_key(obj, key):
|
||||
"""Validate all nested "language" key in `obj`.
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
local fiber = require('fiber')
|
||||
|
||||
box.cfg{listen = 3303}
|
||||
|
||||
box.once("bootstrap", function()
|
||||
@ -171,9 +173,11 @@ function init()
|
||||
utxos = box.schema.create_space('utxos', { if_not_exists = true })
|
||||
utxos:format({
|
||||
{ name = 'id', type = 'string' },
|
||||
{ name = 'transaction_id', type = 'string' },
|
||||
{ name = 'output_index', type = 'unsigned' },
|
||||
{ name = 'utxo', type = 'map' }
|
||||
{ name = 'amount' , type = 'unsigned' },
|
||||
{ name = 'public_keys', type = 'array' },
|
||||
{ name = 'condition', type = 'map' },
|
||||
{ name = 'output_index', type = 'number' },
|
||||
{ name = 'transaction_id' , type = 'string' }
|
||||
})
|
||||
utxos:create_index('id', {
|
||||
if_not_exists = true,
|
||||
@ -189,7 +193,13 @@ function init()
|
||||
parts = {
|
||||
{ field = 'transaction_id', type = 'string' },
|
||||
{ field = 'output_index', type = 'unsigned' }
|
||||
}})
|
||||
}
|
||||
})
|
||||
utxos:create_index('public_keys', {
|
||||
if_not_exists = true,
|
||||
unique = false,
|
||||
parts = {{field = 'public_keys[*]', type = 'string' }}
|
||||
})
|
||||
|
||||
|
||||
-- Elections
|
||||
@ -323,3 +333,65 @@ end
|
||||
function delete_output( id )
|
||||
box.space.outputs:delete(id)
|
||||
end
|
||||
|
||||
function atomic(batch_size, iter, fn)
|
||||
box.atomic(function()
|
||||
local i = 0
|
||||
for _, x in iter:unwrap() do
|
||||
fn(x)
|
||||
i = i + 1
|
||||
if i % batch_size == 0 then
|
||||
box.commit()
|
||||
fiber.yield() -- for read-only operations when `commit` doesn't yield
|
||||
box.begin()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function migrate()
|
||||
-- migration code from 2.4.0 to 2.4.3
|
||||
box.once("planetmint:v2.4.3", function()
|
||||
box.space.utxos:drop()
|
||||
utxos = box.schema.create_space('utxos', { if_not_exists = true })
|
||||
utxos:format({
|
||||
{ name = 'id', type = 'string' },
|
||||
{ name = 'amount' , type = 'unsigned' },
|
||||
{ name = 'public_keys', type = 'array' },
|
||||
{ name = 'condition', type = 'map' },
|
||||
{ name = 'output_index', type = 'number' },
|
||||
{ name = 'transaction_id' , type = 'string' }
|
||||
})
|
||||
utxos:create_index('id', {
|
||||
if_not_exists = true,
|
||||
parts = {{ field = 'id', type = 'string' }}
|
||||
})
|
||||
utxos:create_index('utxos_by_transaction_id', {
|
||||
if_not_exists = true,
|
||||
unique = false,
|
||||
parts = {{ field = 'transaction_id', type = 'string' }}
|
||||
})
|
||||
utxos:create_index('utxo_by_transaction_id_and_output_index', {
|
||||
if_not_exists = true,
|
||||
parts = {
|
||||
{ field = 'transaction_id', type = 'string' },
|
||||
{ field = 'output_index', type = 'unsigned' }
|
||||
}
|
||||
})
|
||||
utxos:create_index('public_keys', {
|
||||
if_not_exists = true,
|
||||
unique = false,
|
||||
parts = {{field = 'public_keys[*]', type = 'string' }}
|
||||
})
|
||||
|
||||
atomic(1000, box.space.outputs:pairs(), function(output)
|
||||
utxos:insert{output[1], output[2], output[3], output[4], output[5], output[6]}
|
||||
end)
|
||||
atomic(1000, utxos:pairs(), function(utxo)
|
||||
spending_transaction = box.space.transactions.index.spending_transaction_by_id_and_output_index:select{utxo[6], utxo[5]}
|
||||
if table.getn(spending_transaction) > 0 then
|
||||
utxos:delete(utxo[1])
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
@ -127,11 +127,12 @@ def get_transactions_by_metadata(connection, metadata: str, limit: int = 1000) -
|
||||
return get_complete_transactions_by_ids(connection, tx_ids)
|
||||
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@catch_db_exception
|
||||
def store_transaction_outputs(connection, output: Output, index: int) -> str:
|
||||
def store_transaction_outputs(connection, output: Output, index: int, table=TARANT_TABLE_OUTPUT) -> str:
|
||||
output_id = uuid4().hex
|
||||
connection.connect().insert(
|
||||
TARANT_TABLE_OUTPUT,
|
||||
table,
|
||||
(
|
||||
output_id,
|
||||
int(output.amount),
|
||||
@ -220,7 +221,9 @@ def get_assets(connection, assets_ids: list) -> list[Asset]:
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@catch_db_exception
|
||||
def get_spent(connection, fullfil_transaction_id: str, fullfil_output_index: str) -> list[DbTransaction]:
|
||||
def get_spending_transaction(
|
||||
connection, fullfil_transaction_id: str, fullfil_output_index: str
|
||||
) -> list[DbTransaction]:
|
||||
_inputs = (
|
||||
connection.connect()
|
||||
.select(
|
||||
@ -300,7 +303,7 @@ def get_spending_transactions(connection, inputs):
|
||||
_transactions = []
|
||||
|
||||
for inp in inputs:
|
||||
_trans_list = get_spent(
|
||||
_trans_list = get_spending_transaction(
|
||||
fullfil_transaction_id=inp["transaction_id"],
|
||||
fullfil_output_index=inp["output_index"],
|
||||
connection=connection,
|
||||
@ -337,6 +340,9 @@ def delete_transactions(connection, txn_ids: list):
|
||||
_outputs = get_outputs_by_tx_id(connection, _id)
|
||||
for x in range(len(_outputs)):
|
||||
connection.connect().call("delete_output", (_outputs[x].id))
|
||||
connection.connect().delete(
|
||||
TARANT_TABLE_UTXOS, (_id, _outputs[x].index), index="utxo_by_transaction_id_and_output_index"
|
||||
)
|
||||
for _id in txn_ids:
|
||||
connection.connect().delete(TARANT_TABLE_TRANSACTION, _id)
|
||||
connection.connect().delete(TARANT_TABLE_GOVERNANCE, _id)
|
||||
@ -344,26 +350,7 @@ def delete_transactions(connection, txn_ids: list):
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@catch_db_exception
|
||||
def store_unspent_outputs(connection, *unspent_outputs: list):
|
||||
result = []
|
||||
if unspent_outputs:
|
||||
for utxo in unspent_outputs:
|
||||
try:
|
||||
output = (
|
||||
connection.connect()
|
||||
.insert(TARANT_TABLE_UTXOS, (uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
|
||||
.data
|
||||
)
|
||||
result.append(output)
|
||||
except Exception as e:
|
||||
logger.info(f"Could not insert unspent output: {e}")
|
||||
raise OperationDataInsertionError()
|
||||
return result
|
||||
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@catch_db_exception
|
||||
def delete_unspent_outputs(connection, *unspent_outputs: list):
|
||||
def delete_unspent_outputs(connection, unspent_outputs: list):
|
||||
result = []
|
||||
if unspent_outputs:
|
||||
for utxo in unspent_outputs:
|
||||
@ -383,8 +370,8 @@ def delete_unspent_outputs(connection, *unspent_outputs: list):
|
||||
@register_query(TarantoolDBConnection)
|
||||
@catch_db_exception
|
||||
def get_unspent_outputs(connection, query=None): # for now we don't have implementation for 'query'.
|
||||
_utxos = connection.connect().select(TARANT_TABLE_UTXOS, []).data
|
||||
return [utx[3] for utx in _utxos]
|
||||
utxos = connection.connect().select(TARANT_TABLE_UTXOS, []).data
|
||||
return [{"transaction_id": utxo[5], "output_index": utxo[4]} for utxo in utxos]
|
||||
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@ -420,11 +407,12 @@ def store_validator_set(conn, validators_update: dict):
|
||||
conn.connect().select(TARANT_TABLE_VALIDATOR_SETS, validators_update["height"], index="height", limit=1).data
|
||||
)
|
||||
unique_id = uuid4().hex if _validator is None or len(_validator) == 0 else _validator[0][0]
|
||||
conn.connect().upsert(
|
||||
result = conn.connect().upsert(
|
||||
TARANT_TABLE_VALIDATOR_SETS,
|
||||
(unique_id, validators_update["height"], validators_update["validators"]),
|
||||
op_list=[("=", 1, validators_update["height"]), ("=", 2, validators_update["validators"])],
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@ -522,3 +510,10 @@ def get_latest_abci_chain(connection) -> Union[dict, None]:
|
||||
return None
|
||||
_chain = sorted(_all_chains, key=itemgetter(1), reverse=True)[0]
|
||||
return {"chain_id": _chain[0], "height": _chain[1], "is_synced": _chain[2]}
|
||||
|
||||
|
||||
@register_query(TarantoolDBConnection)
|
||||
@catch_db_exception
|
||||
def get_outputs_by_owner(connection, public_key: str, table=TARANT_TABLE_OUTPUT) -> list[Output]:
|
||||
outputs = connection.connect().select(table, public_key, index="public_keys")
|
||||
return [Output.from_tuple(output) for output in outputs]
|
||||
|
@ -35,3 +35,8 @@ def create_database(connection, dbname):
|
||||
@register_schema(TarantoolDBConnection)
|
||||
def create_tables(connection, dbname):
|
||||
connection.connect().call("init")
|
||||
|
||||
|
||||
@register_schema(TarantoolDBConnection)
|
||||
def migrate(connection):
|
||||
connection.connect().call("migrate")
|
||||
|
@ -29,7 +29,7 @@ from planetmint.backend import schema
|
||||
from planetmint.commands import utils
|
||||
from planetmint.commands.utils import configure_planetmint, input_on_stderr
|
||||
from planetmint.config_utils import setup_logging
|
||||
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
|
||||
from planetmint.abci.rpc import ABCI_RPC, MODE_COMMIT, MODE_LIST
|
||||
from planetmint.abci.utils import load_node_key, public_key_from_base64
|
||||
from planetmint.commands.election_types import elections
|
||||
from planetmint.version import __tm_supported_versions__
|
||||
@ -111,14 +111,18 @@ def run_election(args):
|
||||
"""Initiate and manage elections"""
|
||||
|
||||
b = Validator()
|
||||
abci_rpc = ABCI_RPC()
|
||||
|
||||
if args.action == "show":
|
||||
run_election_show(args, b)
|
||||
else:
|
||||
# Call the function specified by args.action, as defined above
|
||||
globals()[f"run_election_{args.action}"](args, b)
|
||||
globals()[f"run_election_{args.action}"](args, b, abci_rpc)
|
||||
|
||||
|
||||
def run_election_new(args, planet):
|
||||
def run_election_new(args, planet, abci_rpc):
|
||||
election_type = args.election_type.replace("-", "_")
|
||||
globals()[f"run_election_new_{election_type}"](args, planet)
|
||||
globals()[f"run_election_new_{election_type}"](args, planet, abci_rpc)
|
||||
|
||||
|
||||
def create_new_election(sk, planet, election_class, data, abci_rpc):
|
||||
@ -186,7 +190,7 @@ def run_election_new_chain_migration(args, planet, abci_rpc):
|
||||
return create_new_election(args.sk, planet, ChainMigrationElection, [{"data": {}}], abci_rpc)
|
||||
|
||||
|
||||
def run_election_approve(args, validator: Validator, abci_rpc):
|
||||
def run_election_approve(args, validator: Validator, abci_rpc: ABCI_RPC):
|
||||
"""Approve an election
|
||||
|
||||
:param args: dict
|
||||
@ -258,6 +262,12 @@ def run_init(args):
|
||||
_run_init()
|
||||
|
||||
|
||||
@configure_planetmint
|
||||
def run_migrate(args):
|
||||
validator = Validator()
|
||||
schema.migrate(validator.models.connection)
|
||||
|
||||
|
||||
@configure_planetmint
|
||||
def run_drop(args):
|
||||
"""Drop the database"""
|
||||
@ -363,6 +373,8 @@ def create_parser():
|
||||
|
||||
subparsers.add_parser("drop", help="Drop the database")
|
||||
|
||||
subparsers.add_parser("migrate", help="Migrate up")
|
||||
|
||||
# parser for starting Planetmint
|
||||
start_parser = subparsers.add_parser("start", help="Start Planetmint")
|
||||
|
||||
|
@ -86,7 +86,7 @@ class Config(metaclass=Singleton):
|
||||
"tendermint": {
|
||||
"host": "localhost",
|
||||
"port": 26657,
|
||||
"version": "v0.34.15", # look for __tm_supported_versions__
|
||||
"version": "v0.34.24", # look for __tm_supported_versions__
|
||||
},
|
||||
"database": self.__private_database_map,
|
||||
"log": {
|
||||
@ -117,8 +117,8 @@ class Config(metaclass=Singleton):
|
||||
def set(self, config):
|
||||
self._private_real_config = config
|
||||
|
||||
def get_db_key_map(sefl, db):
|
||||
return sefl.__private_database_keys_map[db]
|
||||
def get_db_key_map(self, db):
|
||||
return self.__private_database_keys_map[db]
|
||||
|
||||
def get_db_map(sefl, db):
|
||||
return sefl.__private_database_map[db]
|
||||
@ -131,16 +131,12 @@ DEFAULT_LOGGING_CONFIG = {
|
||||
"formatters": {
|
||||
"console": {
|
||||
"class": "logging.Formatter",
|
||||
"format": (
|
||||
"[%(asctime)s] [%(levelname)s] (%(name)s) " "%(message)s (%(processName)-10s - pid: %(process)d)"
|
||||
),
|
||||
"format": ("[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)"),
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"file": {
|
||||
"class": "logging.Formatter",
|
||||
"format": (
|
||||
"[%(asctime)s] [%(levelname)s] (%(name)s) " "%(message)s (%(processName)-10s - pid: %(process)d)"
|
||||
),
|
||||
"format": ("[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s (%(processName)-10s - pid: %(process)d)"),
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
import rapidjson
|
||||
from itertools import chain
|
||||
from hashlib import sha3_256
|
||||
|
||||
from transactions import Transaction
|
||||
from transactions.common.exceptions import DoubleSpend
|
||||
@ -8,21 +9,32 @@ from transactions.common.exceptions import InputDoesNotExist
|
||||
|
||||
from planetmint import config_utils, backend
|
||||
from planetmint.const import GOVERNANCE_TRANSACTION_TYPES
|
||||
from planetmint.model.fastquery import FastQuery
|
||||
from planetmint.abci.utils import key_from_base64
|
||||
from planetmint.abci.utils import key_from_base64, merkleroot
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.backend.tarantool.const import TARANT_TABLE_TRANSACTION, TARANT_TABLE_GOVERNANCE
|
||||
from planetmint.backend.tarantool.const import (
|
||||
TARANT_TABLE_TRANSACTION,
|
||||
TARANT_TABLE_GOVERNANCE,
|
||||
TARANT_TABLE_UTXOS,
|
||||
TARANT_TABLE_OUTPUT,
|
||||
)
|
||||
from planetmint.backend.models.block import Block
|
||||
from planetmint.backend.models.output import Output
|
||||
from planetmint.backend.models.asset import Asset
|
||||
from planetmint.backend.models.metadata import MetaData
|
||||
from planetmint.backend.models.dbtransaction import DbTransaction
|
||||
from planetmint.utils.singleton import Singleton
|
||||
|
||||
|
||||
class DataAccessor:
|
||||
def __init__(self, database_connection=None, async_io: bool = False):
|
||||
class DataAccessor(metaclass=Singleton):
|
||||
def __init__(self, database_connection=None):
|
||||
config_utils.autoconfigure()
|
||||
self.connection = database_connection if database_connection is not None else Connection(async_io=async_io)
|
||||
self.connection = database_connection if database_connection is not None else Connection()
|
||||
|
||||
def close_connection(self):
|
||||
self.connection.close()
|
||||
|
||||
def connect(self):
|
||||
self.connection.connect()
|
||||
|
||||
def store_bulk_transactions(self, transactions):
|
||||
txns = []
|
||||
@ -37,6 +49,7 @@ class DataAccessor:
|
||||
|
||||
backend.query.store_transactions(self.connection, txns, TARANT_TABLE_TRANSACTION)
|
||||
backend.query.store_transactions(self.connection, gov_txns, TARANT_TABLE_GOVERNANCE)
|
||||
[self.update_utxoset(t) for t in txns + gov_txns]
|
||||
|
||||
def delete_transactions(self, txs):
|
||||
return backend.query.delete_transactions(self.connection, txs)
|
||||
@ -60,7 +73,7 @@ class DataAccessor:
|
||||
def get_outputs_by_tx_id(self, txid):
|
||||
return backend.query.get_outputs_by_tx_id(self.connection, txid)
|
||||
|
||||
def get_outputs_filtered(self, owner, spent=None):
|
||||
def get_outputs_filtered(self, owner, spent=None) -> list[Output]:
|
||||
"""Get a list of output links filtered on some criteria
|
||||
|
||||
Args:
|
||||
@ -70,16 +83,23 @@ class DataAccessor:
|
||||
not specified (``None``) return all outputs.
|
||||
|
||||
Returns:
|
||||
:obj:`list` of TransactionLink: list of ``txid`` s and ``output`` s
|
||||
:obj:`list` of Output: list of ``txid`` s and ``output`` s
|
||||
pointing to another transaction's condition
|
||||
"""
|
||||
outputs = self.fastquery.get_outputs_by_public_key(owner)
|
||||
if spent is None:
|
||||
return outputs
|
||||
elif spent is True:
|
||||
return self.fastquery.filter_unspent_outputs(outputs)
|
||||
outputs = backend.query.get_outputs_by_owner(self.connection, owner)
|
||||
unspent_outputs = backend.query.get_outputs_by_owner(self.connection, owner, TARANT_TABLE_UTXOS)
|
||||
if spent is True:
|
||||
spent_outputs = []
|
||||
for output in outputs:
|
||||
if not any(
|
||||
utxo.transaction_id == output.transaction_id and utxo.index == output.index
|
||||
for utxo in unspent_outputs
|
||||
):
|
||||
spent_outputs.append(output)
|
||||
return spent_outputs
|
||||
elif spent is False:
|
||||
return self.fastquery.filter_spent_outputs(outputs)
|
||||
return unspent_outputs
|
||||
return outputs
|
||||
|
||||
def store_block(self, block):
|
||||
"""Create a new block."""
|
||||
@ -131,15 +151,15 @@ class DataAccessor:
|
||||
value as the `voting_power`
|
||||
"""
|
||||
validators = {}
|
||||
for validator in self.get_validators(height):
|
||||
for validator in self.get_validators(height=height):
|
||||
# NOTE: we assume that Tendermint encodes public key in base64
|
||||
public_key = public_key_from_ed25519_key(key_from_base64(validator["public_key"]["value"]))
|
||||
validators[public_key] = validator["voting_power"]
|
||||
|
||||
return validators
|
||||
|
||||
def get_spent(self, txid, output, current_transactions=[]) -> DbTransaction:
|
||||
transactions = backend.query.get_spent(self.connection, txid, output)
|
||||
def get_spending_transaction(self, txid, output, current_transactions=[]) -> DbTransaction:
|
||||
transactions = backend.query.get_spending_transaction(self.connection, txid, output)
|
||||
|
||||
current_spent_transactions = []
|
||||
for ctxn in current_transactions:
|
||||
@ -196,7 +216,7 @@ class DataAccessor:
|
||||
if input_tx is None:
|
||||
raise InputDoesNotExist("input `{}` doesn't exist".format(input_txid))
|
||||
|
||||
spent = self.get_spent(input_txid, input_.fulfills.output, current_transactions)
|
||||
spent = self.get_spending_transaction(input_txid, input_.fulfills.output, current_transactions)
|
||||
if spent:
|
||||
raise DoubleSpend("input `{}` was already spent".format(input_txid))
|
||||
|
||||
@ -277,6 +297,52 @@ class DataAccessor:
|
||||
txns = backend.query.get_asset_tokens_for_public_key(self.connection, transaction_id, election_pk)
|
||||
return txns
|
||||
|
||||
@property
|
||||
def fastquery(self):
|
||||
return FastQuery(self.connection)
|
||||
def update_utxoset(self, transaction):
|
||||
spent_outputs = [
|
||||
{"output_index": input["fulfills"]["output_index"], "transaction_id": input["fulfills"]["transaction_id"]}
|
||||
for input in transaction["inputs"]
|
||||
if input["fulfills"] != None
|
||||
]
|
||||
|
||||
if spent_outputs:
|
||||
backend.query.delete_unspent_outputs(self.connection, spent_outputs)
|
||||
[
|
||||
backend.query.store_transaction_outputs(
|
||||
self.connection, Output.outputs_dict(output, transaction["id"]), index, TARANT_TABLE_UTXOS
|
||||
)
|
||||
for index, output in enumerate(transaction["outputs"])
|
||||
]
|
||||
|
||||
def get_utxoset_merkle_root(self):
|
||||
"""Returns the merkle root of the utxoset. This implies that
|
||||
the utxoset is first put into a merkle tree.
|
||||
|
||||
For now, the merkle tree and its root will be computed each
|
||||
time. This obviously is not efficient and a better approach
|
||||
that limits the repetition of the same computation when
|
||||
unnecesary should be sought. For instance, future optimizations
|
||||
could simply re-compute the branches of the tree that were
|
||||
affected by a change.
|
||||
|
||||
The transaction hash (id) and output index should be sufficient
|
||||
to uniquely identify a utxo, and consequently only that
|
||||
information from a utxo record is needed to compute the merkle
|
||||
root. Hence, each node of the merkle tree should contain the
|
||||
tuple (txid, output_index).
|
||||
|
||||
.. important:: The leaves of the tree will need to be sorted in
|
||||
some kind of lexicographical order.
|
||||
|
||||
Returns:
|
||||
str: Merkle root in hexadecimal form.
|
||||
"""
|
||||
utxoset = backend.query.get_unspent_outputs(self.connection)
|
||||
# See common/transactions.py for details.
|
||||
|
||||
hashes = [
|
||||
sha3_256("{}{}".format(utxo["transaction_id"], utxo["output_index"]).encode()).digest() for utxo in utxoset
|
||||
]
|
||||
|
||||
print(sorted(hashes))
|
||||
|
||||
return merkleroot(sorted(hashes))
|
||||
|
@ -1,76 +0,0 @@
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
from planetmint.backend import query
|
||||
from transactions.common.transaction import TransactionLink
|
||||
|
||||
from planetmint.backend.models.output import ConditionDetails
|
||||
|
||||
|
||||
class FastQuery:
|
||||
"""Database queries that join on block results from a single node."""
|
||||
|
||||
def __init__(self, connection):
|
||||
self.connection = connection
|
||||
|
||||
def get_outputs_by_public_key(self, public_key):
|
||||
"""Get outputs for a public key"""
|
||||
txs = query.get_owned_ids(self.connection, public_key)
|
||||
return [
|
||||
TransactionLink(tx.id, index)
|
||||
for tx in txs
|
||||
for index, output in enumerate(tx.outputs)
|
||||
if condition_details_has_owner(output.condition.details, public_key)
|
||||
]
|
||||
|
||||
def filter_spent_outputs(self, outputs):
|
||||
"""Remove outputs that have been spent
|
||||
|
||||
Args:
|
||||
outputs: list of TransactionLink
|
||||
"""
|
||||
links = [o.to_dict() for o in outputs]
|
||||
txs = query.get_spending_transactions(self.connection, links)
|
||||
spends = {TransactionLink.from_dict(input.fulfills.to_dict()) for tx in txs for input in tx.inputs}
|
||||
return [ff for ff in outputs if ff not in spends]
|
||||
|
||||
def filter_unspent_outputs(self, outputs):
|
||||
"""Remove outputs that have not been spent
|
||||
|
||||
Args:
|
||||
outputs: list of TransactionLink
|
||||
"""
|
||||
links = [o.to_dict() for o in outputs]
|
||||
txs = query.get_spending_transactions(self.connection, links)
|
||||
spends = {TransactionLink.from_dict(input.fulfills.to_dict()) for tx in txs for input in tx.inputs}
|
||||
return [ff for ff in outputs if ff in spends]
|
||||
|
||||
|
||||
# TODO: Rename this function, it's handling fulfillments not conditions
|
||||
def condition_details_has_owner(condition_details, owner):
|
||||
"""Check if the public_key of owner is in the condition details
|
||||
as an Ed25519Fulfillment.public_key
|
||||
|
||||
Args:
|
||||
condition_details (dict): dict with condition details
|
||||
owner (str): base58 public key of owner
|
||||
|
||||
Returns:
|
||||
bool: True if the public key is found in the condition details, False otherwise
|
||||
|
||||
"""
|
||||
if isinstance(condition_details, ConditionDetails) and condition_details.sub_conditions is not None:
|
||||
result = condition_details_has_owner(condition_details.sub_conditions, owner)
|
||||
if result:
|
||||
return True
|
||||
elif isinstance(condition_details, list):
|
||||
for subcondition in condition_details:
|
||||
result = condition_details_has_owner(subcondition, owner)
|
||||
if result:
|
||||
return True
|
||||
else:
|
||||
if condition_details.public_key is not None and owner == condition_details.public_key:
|
||||
return True
|
||||
return False
|
@ -3,6 +3,7 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
import sys
|
||||
import logging
|
||||
import setproctitle
|
||||
|
||||
@ -94,4 +95,4 @@ def start(args):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start()
|
||||
start(sys.argv)
|
||||
|
@ -3,8 +3,8 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
__version__ = "2.3.3"
|
||||
__short_version__ = "2.3"
|
||||
__version__ = "2.5.1"
|
||||
__short_version__ = "2.5"
|
||||
|
||||
# Supported Tendermint versions
|
||||
__tm_supported_versions__ = ["0.34.15"]
|
||||
__tm_supported_versions__ = ["0.34.24"]
|
||||
|
@ -3,8 +3,7 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
"""Common classes and methods for API handlers
|
||||
"""
|
||||
"""Common classes and methods for API handlers"""
|
||||
import logging
|
||||
|
||||
from flask import jsonify, request
|
||||
|
@ -32,4 +32,4 @@ class OutputListApi(Resource):
|
||||
"Invalid output ({}): {} : {} - {}".format(type(e).__name__, e, args["public_key"], args["spent"]),
|
||||
level="error",
|
||||
)
|
||||
return [{"transaction_id": output.txid, "output_index": output.output} for output in outputs]
|
||||
return [{"transaction_id": output.transaction_id, "output_index": output.index} for output in outputs]
|
||||
|
@ -97,7 +97,7 @@ class TransactionListApi(Resource):
|
||||
500, "Invalid transaction ({}): {} : {}".format(type(e).__name__, e, tx), level="error"
|
||||
)
|
||||
else:
|
||||
if tx_obj.version != Transaction.VERSION:
|
||||
if tx_obj.version != Transaction.__VERSION__:
|
||||
return make_error(
|
||||
401,
|
||||
"Invalid transaction version: The transaction is valid, \
|
||||
|
2868
poetry.lock
generated
2868
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "planetmint"
|
||||
version = "2.4.0"
|
||||
version = "2.5.3"
|
||||
description = "Planetmint: The Blockchain Database"
|
||||
authors = ["Planetmint contributors"]
|
||||
license = "AGPLv3"
|
||||
@ -25,7 +25,7 @@ planetmint = "planetmint.commands.planetmint:main"
|
||||
python = "^3.9"
|
||||
chardet = "3.0.4"
|
||||
base58 = "2.1.1"
|
||||
aiohttp = "^3.8.4"
|
||||
aiohttp = "3.9.5"
|
||||
flask-cors = "3.0.10"
|
||||
flask-restful = "0.3.9"
|
||||
flask = "2.1.2"
|
||||
@ -36,8 +36,8 @@ packaging = ">=22.0"
|
||||
pymongo = "3.11.4"
|
||||
tarantool = ">=0.12.1"
|
||||
python-rapidjson = ">=1.0"
|
||||
pyyaml = "6.0.0"
|
||||
requests = "2.25.1"
|
||||
pyyaml = "6.0.2"
|
||||
requests = "2.31.0"
|
||||
setproctitle = "1.2.2"
|
||||
werkzeug = "2.0.3"
|
||||
nest-asyncio = "1.5.5"
|
||||
@ -45,15 +45,16 @@ protobuf = "3.20.2"
|
||||
planetmint-ipld = ">=0.0.3"
|
||||
pyasn1 = ">=0.4.8"
|
||||
python-decouple = "^3.7"
|
||||
planetmint-transactions = ">=0.8.0"
|
||||
planetmint-transactions = ">=0.8.1"
|
||||
asynctnt = "^2.0.1"
|
||||
abci = "^0.8.3"
|
||||
planetmint-abci = "^0.8.4"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
aafigure = "0.6"
|
||||
alabaster = "0.7.12"
|
||||
babel = "2.10.1"
|
||||
certifi = "2022.12.7"
|
||||
certifi = "2023.7.22"
|
||||
charset-normalizer = "2.0.12"
|
||||
commonmark = "0.9.1"
|
||||
docutils = "0.17.1"
|
||||
@ -67,7 +68,7 @@ mdit-py-plugins = "0.3.0"
|
||||
mdurl = "0.1.1"
|
||||
myst-parser = "0.17.2"
|
||||
pockets = "0.9.1"
|
||||
pygments = "2.12.0"
|
||||
pygments = "2.15.0"
|
||||
pyparsing = "3.0.8"
|
||||
pytz = "2022.1"
|
||||
pyyaml = ">=5.4.0"
|
||||
@ -83,7 +84,7 @@ sphinxcontrib-jsmath = "1.0.1"
|
||||
sphinxcontrib-napoleon = "0.7"
|
||||
sphinxcontrib-qthelp = "1.0.3"
|
||||
sphinxcontrib-serializinghtml = "1.1.5"
|
||||
urllib3 = "1.26.9"
|
||||
urllib3 = "1.26.18"
|
||||
wget = "3.2"
|
||||
zipp = "3.8.0"
|
||||
nest-asyncio = "1.5.5"
|
||||
@ -105,8 +106,9 @@ pytest-cov = "2.8.1"
|
||||
pytest-mock = "^3.10.0"
|
||||
pytest-xdist = "^3.1.0"
|
||||
pytest-flask = "^1.2.0"
|
||||
pytest-aiohttp = "^1.0.4"
|
||||
pytest-asyncio = "^0.20.3"
|
||||
pytest-aiohttp = "1.0.4"
|
||||
pytest-asyncio = "0.19.0"
|
||||
pip-audit = "^2.5.6"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
@ -1,9 +1,9 @@
|
||||
[pytest]
|
||||
testpaths = tests/
|
||||
norecursedirs = .* *.egg *.egg-info env* devenv* docs
|
||||
addopts = -m "abci"
|
||||
addopts = -m "not abci"
|
||||
looponfailroots = planetmint tests
|
||||
asyncio_mode = strict
|
||||
asyncio_mode = auto
|
||||
markers =
|
||||
bdb: bdb
|
||||
skip: skip
|
||||
|
@ -1,3 +0,0 @@
|
||||
sonar.projectKey=planetmint_planetmint_AYdLgEyUjRMsrlXgCln1
|
||||
sonar.python.version=3.9
|
||||
sonar.exclusions=k8s/**
|
@ -157,14 +157,14 @@ def test_single_in_single_own_multiple_out_single_own_transfer(alice, b, user_pk
|
||||
)
|
||||
tx_create_signed = tx_create.sign([alice.private_key])
|
||||
|
||||
b.models.store_bulk_transactions([tx_create_signed])
|
||||
inputs = tx_create.to_inputs()
|
||||
# TRANSFER
|
||||
tx_transfer = Transfer.generate(
|
||||
tx_create.to_inputs(), [([alice.public_key], 50), ([alice.public_key], 50)], asset_ids=[tx_create.id]
|
||||
inputs, [([alice.public_key], 50), ([alice.public_key], 50)], asset_ids=[tx_create.id]
|
||||
)
|
||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||
|
||||
b.models.store_bulk_transactions([tx_create_signed])
|
||||
|
||||
assert b.validate_transaction(tx_transfer_signed) == tx_transfer_signed
|
||||
assert len(tx_transfer_signed.outputs) == 2
|
||||
assert tx_transfer_signed.outputs[0].amount == 50
|
||||
|
@ -1,5 +1,6 @@
|
||||
import json
|
||||
import base58
|
||||
import pytest
|
||||
|
||||
from hashlib import sha3_256
|
||||
from planetmint_cryptoconditions.types.ed25519 import Ed25519Sha256
|
||||
@ -31,6 +32,7 @@ metadata = {"units": 300, "type": "KG"}
|
||||
SCRIPT_OUTPUTS = ["ok"]
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="new zenroom adjusteds have to be made")
|
||||
def test_zenroom_validation(b):
|
||||
biolabs = generate_key_pair()
|
||||
version = "3.0"
|
||||
@ -92,26 +94,7 @@ def test_zenroom_validation(b):
|
||||
tx["id"] = shared_creation_txid
|
||||
|
||||
from transactions.common.transaction import Transaction
|
||||
from transactions.common.exceptions import (
|
||||
SchemaValidationError,
|
||||
ValidationError,
|
||||
)
|
||||
|
||||
try:
|
||||
print(f"TX\n{tx}")
|
||||
tx_obj = Transaction.from_dict(tx, False)
|
||||
except SchemaValidationError as e:
|
||||
print(e)
|
||||
assert ()
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
assert ()
|
||||
|
||||
try:
|
||||
b.validate_transaction(tx_obj)
|
||||
except ValidationError as e:
|
||||
print("Invalid transaction ({}): {}".format(type(e).__name__, e))
|
||||
assert ()
|
||||
|
||||
print(f"VALIDATED : {tx_obj}")
|
||||
assert (tx_obj == False) is False
|
||||
|
@ -29,7 +29,7 @@ def test_schema(schema_func_name, args_qty):
|
||||
("get_txids_filtered", 1),
|
||||
("get_owned_ids", 1),
|
||||
("get_block", 1),
|
||||
("get_spent", 2),
|
||||
("get_spending_transaction", 2),
|
||||
("get_spending_transactions", 1),
|
||||
("store_assets", 1),
|
||||
("get_asset", 1),
|
||||
|
@ -26,6 +26,106 @@ from planetmint.backend.connection import Connection
|
||||
from tests.utils import generate_election, generate_validators
|
||||
|
||||
|
||||
rpc_write_transaction_string = "planetmint.abci.rpc.ABCI_RPC.write_transaction"
|
||||
|
||||
|
||||
def mock_get_validators(self, height):
|
||||
return [
|
||||
{
|
||||
"public_key": {
|
||||
"value": "zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=",
|
||||
"type": "ed25519-base64",
|
||||
},
|
||||
"voting_power": 10,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@patch("planetmint.commands.utils.start")
|
||||
def test_main_entrypoint(mock_start):
|
||||
from planetmint.commands.planetmint import main
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
da = DataAccessor
|
||||
del da
|
||||
main()
|
||||
|
||||
assert mock_start.called
|
||||
|
||||
|
||||
# @pytest.mark.bdb
|
||||
def test_chain_migration_election_show_shows_inconclusive(b_flushed, test_abci_rpc):
|
||||
from tests.utils import flush_db
|
||||
|
||||
b = b_flushed
|
||||
|
||||
validators = generate_validators([1] * 4)
|
||||
_ = b.models.store_validator_set(1, [v["storage"] for v in validators])
|
||||
|
||||
public_key = validators[0]["public_key"]
|
||||
private_key = validators[0]["private_key"]
|
||||
voter_keys = [v["private_key"] for v in validators]
|
||||
|
||||
election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys)
|
||||
|
||||
assert not run_election_show(Namespace(election_id=election.id), b)
|
||||
|
||||
b.process_block(1, [election])
|
||||
b.models.store_bulk_transactions([election])
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
|
||||
|
||||
b.models.store_block(Block(height=1, transactions=[], app_hash="")._asdict())
|
||||
b.models.store_validator_set(2, [v["storage"] for v in validators])
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
|
||||
|
||||
b.models.store_block(Block(height=2, transactions=[], app_hash="")._asdict())
|
||||
# TODO insert yet another block here when upgrading to Tendermint 0.22.4.
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=inconclusive"
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_chain_migration_election_show_shows_concluded(b_flushed):
|
||||
b = b_flushed
|
||||
validators = generate_validators([1] * 4)
|
||||
b.models.store_validator_set(1, [v["storage"] for v in validators])
|
||||
|
||||
public_key = validators[0]["public_key"]
|
||||
private_key = validators[0]["private_key"]
|
||||
voter_keys = [v["private_key"] for v in validators]
|
||||
|
||||
election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys)
|
||||
|
||||
assert not run_election_show(Namespace(election_id=election.id), b)
|
||||
|
||||
b.models.store_bulk_transactions([election])
|
||||
b.process_block(1, [election])
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
|
||||
|
||||
b.models.store_abci_chain(1, "chain-X")
|
||||
b.models.store_block(Block(height=1, transactions=[v.id for v in votes], app_hash="last_app_hash")._asdict())
|
||||
b.process_block(2, votes)
|
||||
|
||||
assert (
|
||||
run_election_show(Namespace(election_id=election.id), b)
|
||||
== f'''status=concluded
|
||||
chain_id=chain-X-migrated-at-height-1
|
||||
app_hash=last_app_hash
|
||||
validators=[{''.join([f"""
|
||||
{{
|
||||
"pub_key": {{
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "{v['public_key']}"
|
||||
}},
|
||||
"power": {v['storage']['voting_power']}
|
||||
}}{',' if i + 1 != len(validators) else ''}""" for i, v in enumerate(validators)])}
|
||||
]'''
|
||||
)
|
||||
|
||||
|
||||
def test_make_sure_we_dont_remove_any_command():
|
||||
# thanks to: http://stackoverflow.com/a/18161115/597097
|
||||
from planetmint.commands.planetmint import create_parser
|
||||
@ -50,22 +150,44 @@ def test_make_sure_we_dont_remove_any_command():
|
||||
]
|
||||
).command
|
||||
assert parser.parse_args(
|
||||
["election", "new", "chain-migration", "--private-key", "TEMP_PATH_TO_PRIVATE_KEY"]
|
||||
[
|
||||
"election",
|
||||
"new",
|
||||
"chain-migration",
|
||||
"--private-key",
|
||||
"TEMP_PATH_TO_PRIVATE_KEY",
|
||||
]
|
||||
).command
|
||||
assert parser.parse_args(
|
||||
["election", "approve", "ELECTION_ID", "--private-key", "TEMP_PATH_TO_PRIVATE_KEY"]
|
||||
[
|
||||
"election",
|
||||
"approve",
|
||||
"ELECTION_ID",
|
||||
"--private-key",
|
||||
"TEMP_PATH_TO_PRIVATE_KEY",
|
||||
]
|
||||
).command
|
||||
assert parser.parse_args(["election", "show", "ELECTION_ID"]).command
|
||||
assert parser.parse_args(["tendermint-version"]).command
|
||||
|
||||
|
||||
@patch("planetmint.commands.utils.start")
|
||||
def test_main_entrypoint(mock_start):
|
||||
from planetmint.commands.planetmint import main
|
||||
@pytest.mark.bdb
|
||||
def test_election_approve_called_with_bad_key(
|
||||
monkeypatch, caplog, b, bad_validator_path, new_validator, node_key, test_abci_rpc
|
||||
):
|
||||
from argparse import Namespace
|
||||
|
||||
main()
|
||||
b, election_id = call_election(monkeypatch, b, new_validator, node_key, test_abci_rpc)
|
||||
|
||||
assert mock_start.called
|
||||
# call run_upsert_validator_approve with args that point to the election, but a bad signing key
|
||||
args = Namespace(action="approve", election_id=election_id, sk=bad_validator_path, config={})
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
assert not run_election_approve(args, b, test_abci_rpc)
|
||||
assert (
|
||||
caplog.records[0].msg == "The key you provided does not match any of "
|
||||
"the eligible voters in this election."
|
||||
)
|
||||
|
||||
|
||||
@patch("planetmint.config_utils.setup_logging")
|
||||
@ -168,7 +290,10 @@ def test_drop_db_does_not_drop_when_interactive_no(mock_db_drop, monkeypatch):
|
||||
# switch with pytest. It will just hang. Seems related to the monkeypatching of
|
||||
# input_on_stderr.
|
||||
def test_run_configure_when_config_does_not_exist(
|
||||
monkeypatch, mock_write_config, mock_generate_key_pair, mock_planetmint_backup_config
|
||||
monkeypatch,
|
||||
mock_write_config,
|
||||
mock_generate_key_pair,
|
||||
mock_planetmint_backup_config,
|
||||
):
|
||||
from planetmint.commands.planetmint import run_configure
|
||||
|
||||
@ -180,7 +305,10 @@ def test_run_configure_when_config_does_not_exist(
|
||||
|
||||
|
||||
def test_run_configure_when_config_does_exist(
|
||||
monkeypatch, mock_write_config, mock_generate_key_pair, mock_planetmint_backup_config
|
||||
monkeypatch,
|
||||
mock_write_config,
|
||||
mock_generate_key_pair,
|
||||
mock_planetmint_backup_config,
|
||||
):
|
||||
value = {}
|
||||
|
||||
@ -329,13 +457,18 @@ def test_election_new_upsert_validator_with_tendermint(b, priv_validator_path, u
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_election_new_upsert_validator_without_tendermint(caplog, b, priv_validator_path, user_sk, test_abci_rpc):
|
||||
def mock_write(modelist, endpoint, mode_commit, transaction, mode):
|
||||
def test_election_new_upsert_validator_without_tendermint(
|
||||
monkeypatch, caplog, b, priv_validator_path, user_sk, test_abci_rpc
|
||||
):
|
||||
def mock_write(self, modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
return (202, "")
|
||||
|
||||
b.models.get_validators = mock_get_validators
|
||||
test_abci_rpc.write_transaction = mock_write
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
m.setattr(rpc_write_transaction_string, mock_write)
|
||||
|
||||
args = Namespace(
|
||||
action="new",
|
||||
@ -351,6 +484,7 @@ def test_election_new_upsert_validator_without_tendermint(caplog, b, priv_valida
|
||||
election_id = run_election_new_upsert_validator(args, b, test_abci_rpc)
|
||||
assert caplog.records[0].msg == "[SUCCESS] Submitted proposal with id: " + election_id
|
||||
assert b.models.get_transaction(election_id)
|
||||
m.undo()
|
||||
|
||||
|
||||
@pytest.mark.abci
|
||||
@ -363,13 +497,18 @@ def test_election_new_chain_migration_with_tendermint(b, priv_validator_path, us
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_election_new_chain_migration_without_tendermint(caplog, b, priv_validator_path, user_sk, test_abci_rpc):
|
||||
def mock_write(modelist, endpoint, mode_commit, transaction, mode):
|
||||
def test_election_new_chain_migration_without_tendermint(
|
||||
monkeypatch, caplog, b, priv_validator_path, user_sk, test_abci_rpc
|
||||
):
|
||||
def mock_write(self, modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
return (202, "")
|
||||
|
||||
b.models.get_validators = mock_get_validators
|
||||
test_abci_rpc.write_transaction = mock_write
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
m.setattr(rpc_write_transaction_string, mock_write)
|
||||
|
||||
args = Namespace(action="new", election_type="migration", sk=priv_validator_path, config={})
|
||||
|
||||
@ -397,15 +536,21 @@ def test_election_new_upsert_validator_invalid_election(caplog, b, priv_validato
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_election_new_upsert_validator_invalid_power(caplog, b, priv_validator_path, user_sk, test_abci_rpc):
|
||||
def test_election_new_upsert_validator_invalid_power(
|
||||
monkeypatch, caplog, b, priv_validator_path, user_sk, test_abci_rpc
|
||||
):
|
||||
from transactions.common.exceptions import InvalidPowerChange
|
||||
|
||||
def mock_write(modelist, endpoint, mode_commit, transaction, mode):
|
||||
def mock_write(self, modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
return (400, "")
|
||||
|
||||
test_abci_rpc.write_transaction = mock_write
|
||||
b.models.get_validators = mock_get_validators
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
m.setattr(rpc_write_transaction_string, mock_write)
|
||||
|
||||
args = Namespace(
|
||||
action="new",
|
||||
election_type="upsert-validator",
|
||||
@ -444,11 +589,23 @@ def test_election_approve_with_tendermint(b, priv_validator_path, user_sk, valid
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_election_approve_without_tendermint(caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc):
|
||||
def test_election_approve_without_tendermint(
|
||||
monkeypatch, caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc
|
||||
):
|
||||
def mock_write(self, modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
return (202, "")
|
||||
|
||||
from planetmint.commands.planetmint import run_election_approve
|
||||
from argparse import Namespace
|
||||
|
||||
b, election_id = call_election(b, new_validator, node_key, test_abci_rpc)
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
m.setattr(rpc_write_transaction_string, mock_write)
|
||||
|
||||
b, election_id = call_election_internal(b, new_validator, node_key)
|
||||
|
||||
# call run_election_approve with args that point to the election
|
||||
args = Namespace(action="approve", election_id=election_id, sk=priv_validator_path, config={})
|
||||
@ -458,13 +615,17 @@ def test_election_approve_without_tendermint(caplog, b, priv_validator_path, new
|
||||
approval_id = run_election_approve(args, b, test_abci_rpc)
|
||||
assert caplog.records[0].msg == "[SUCCESS] Your vote has been submitted"
|
||||
assert b.models.get_transaction(approval_id)
|
||||
m.undo()
|
||||
|
||||
|
||||
from unittest import mock
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_election_approve_failure(caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc):
|
||||
def test_election_approve_failure(monkeypatch, caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc):
|
||||
from argparse import Namespace
|
||||
|
||||
b, election_id = call_election(b, new_validator, node_key, test_abci_rpc)
|
||||
b, election_id = call_election(monkeypatch, b, new_validator, node_key, test_abci_rpc)
|
||||
|
||||
def mock_write(modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
@ -480,91 +641,6 @@ def test_election_approve_failure(caplog, b, priv_validator_path, new_validator,
|
||||
assert caplog.records[0].msg == "Failed to commit vote"
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_election_approve_called_with_bad_key(caplog, b, bad_validator_path, new_validator, node_key, test_abci_rpc):
|
||||
from argparse import Namespace
|
||||
|
||||
b, election_id = call_election(b, new_validator, node_key, test_abci_rpc)
|
||||
|
||||
# call run_upsert_validator_approve with args that point to the election, but a bad signing key
|
||||
args = Namespace(action="approve", election_id=election_id, sk=bad_validator_path, config={})
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
assert not run_election_approve(args, b, test_abci_rpc)
|
||||
assert (
|
||||
caplog.records[0].msg == "The key you provided does not match any of "
|
||||
"the eligible voters in this election."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_chain_migration_election_show_shows_inconclusive(b):
|
||||
validators = generate_validators([1] * 4)
|
||||
b.models.store_validator_set(1, [v["storage"] for v in validators])
|
||||
|
||||
public_key = validators[0]["public_key"]
|
||||
private_key = validators[0]["private_key"]
|
||||
voter_keys = [v["private_key"] for v in validators]
|
||||
|
||||
election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys)
|
||||
|
||||
assert not run_election_show(Namespace(election_id=election.id), b)
|
||||
|
||||
b.process_block(1, [election])
|
||||
b.models.store_bulk_transactions([election])
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
|
||||
|
||||
b.models.store_block(Block(height=1, transactions=[], app_hash="")._asdict())
|
||||
b.models.store_validator_set(2, [v["storage"] for v in validators])
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
|
||||
|
||||
b.models.store_block(Block(height=2, transactions=[], app_hash="")._asdict())
|
||||
# TODO insert yet another block here when upgrading to Tendermint 0.22.4.
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=inconclusive"
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_chain_migration_election_show_shows_concluded(b):
|
||||
validators = generate_validators([1] * 4)
|
||||
b.models.store_validator_set(1, [v["storage"] for v in validators])
|
||||
|
||||
public_key = validators[0]["public_key"]
|
||||
private_key = validators[0]["private_key"]
|
||||
voter_keys = [v["private_key"] for v in validators]
|
||||
|
||||
election, votes = generate_election(b, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys)
|
||||
|
||||
assert not run_election_show(Namespace(election_id=election.id), b)
|
||||
|
||||
b.models.store_bulk_transactions([election])
|
||||
b.process_block(1, [election])
|
||||
|
||||
assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing"
|
||||
|
||||
b.models.store_abci_chain(1, "chain-X")
|
||||
b.models.store_block(Block(height=1, transactions=[v.id for v in votes], app_hash="last_app_hash")._asdict())
|
||||
b.process_block(2, votes)
|
||||
|
||||
assert (
|
||||
run_election_show(Namespace(election_id=election.id), b)
|
||||
== f'''status=concluded
|
||||
chain_id=chain-X-migrated-at-height-1
|
||||
app_hash=last_app_hash
|
||||
validators=[{''.join([f"""
|
||||
{{
|
||||
"pub_key": {{
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
"value": "{v['public_key']}"
|
||||
}},
|
||||
"power": {v['storage']['voting_power']}
|
||||
}}{',' if i + 1 != len(validators) else ''}""" for i, v in enumerate(validators)])}
|
||||
]'''
|
||||
)
|
||||
|
||||
|
||||
def test_bigchain_tendermint_version(capsys):
|
||||
from planetmint.commands.planetmint import run_tendermint_version
|
||||
|
||||
@ -578,24 +654,7 @@ def test_bigchain_tendermint_version(capsys):
|
||||
assert sorted(output_config["tendermint"]) == sorted(__tm_supported_versions__)
|
||||
|
||||
|
||||
def mock_get_validators(height):
|
||||
return [
|
||||
{
|
||||
"public_key": {"value": "zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=", "type": "ed25519-base64"},
|
||||
"voting_power": 10,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def call_election(b, new_validator, node_key, abci_rpc):
|
||||
def mock_write(modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
return (202, "")
|
||||
|
||||
# patch the validator set. We now have one validator with power 10
|
||||
b.models.get_validators = mock_get_validators
|
||||
abci_rpc.write_transaction = mock_write
|
||||
|
||||
def call_election_internal(b, new_validator, node_key):
|
||||
# our voters is a list of length 1, populated from our mocked validator
|
||||
voters = b.get_recipients_list()
|
||||
# and our voter is the public key from the voter list
|
||||
@ -607,3 +666,18 @@ def call_election(b, new_validator, node_key, abci_rpc):
|
||||
b.models.store_bulk_transactions([valid_election])
|
||||
|
||||
return b, election_id
|
||||
|
||||
|
||||
def call_election(monkeypatch, b, new_validator, node_key, abci_rpc):
|
||||
def mock_write(self, modelist, endpoint, mode_commit, transaction, mode):
|
||||
b.models.store_bulk_transactions([transaction])
|
||||
return (202, "")
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
m.setattr(rpc_write_transaction_string, mock_write)
|
||||
b, election_id = call_election_internal(b, new_validator, node_key)
|
||||
m.undo()
|
||||
return b, election_id
|
||||
|
@ -27,7 +27,10 @@ from transactions.common import crypto
|
||||
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
||||
from planetmint.abci.utils import key_from_base64
|
||||
from planetmint.backend import schema, query
|
||||
from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
|
||||
from transactions.common.crypto import (
|
||||
key_pair_from_ed25519_key,
|
||||
public_key_from_ed25519_key,
|
||||
)
|
||||
from planetmint.abci.block import Block
|
||||
from planetmint.abci.rpc import MODE_LIST
|
||||
from tests.utils import gen_vote
|
||||
@ -107,7 +110,10 @@ def _configure_planetmint(request):
|
||||
# backend = request.config.getoption('--database-backend')
|
||||
backend = "tarantool_db"
|
||||
|
||||
config = {"database": Config().get_db_map(backend), "tendermint": Config()._private_real_config["tendermint"]}
|
||||
config = {
|
||||
"database": Config().get_db_map(backend),
|
||||
"tendermint": Config()._private_real_config["tendermint"],
|
||||
}
|
||||
config["database"]["name"] = test_db_name
|
||||
config = config_utils.env_config(config)
|
||||
config_utils.set_config(config)
|
||||
@ -133,6 +139,28 @@ def _setup_database(_configure_planetmint): # TODO Here is located setup databa
|
||||
print("Finished deleting `{}`".format(dbname))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def da_reset(_setup_database):
|
||||
from transactions.common.memoize import to_dict, from_dict
|
||||
from transactions.common.transaction import Transaction
|
||||
from .utils import flush_db
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
da = DataAccessor()
|
||||
del da
|
||||
da = DataAccessor()
|
||||
da.close_connection()
|
||||
da.connect()
|
||||
|
||||
yield
|
||||
dbname = Config().get()["database"]["name"]
|
||||
flush_db(da.connection, dbname)
|
||||
|
||||
to_dict.cache_clear()
|
||||
from_dict.cache_clear()
|
||||
Transaction._input_valid.cache_clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def _bdb(_setup_database):
|
||||
from transactions.common.memoize import to_dict, from_dict
|
||||
@ -273,6 +301,38 @@ def test_abci_rpc():
|
||||
def b():
|
||||
from planetmint.application import Validator
|
||||
|
||||
old_validator_instance = Validator()
|
||||
del old_validator_instance.models
|
||||
del old_validator_instance
|
||||
validator = Validator()
|
||||
validator.models.connection.close()
|
||||
validator.models.connection.connect()
|
||||
return validator
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def b_flushed(_setup_database):
|
||||
from planetmint.application import Validator
|
||||
from transactions.common.memoize import to_dict, from_dict
|
||||
from transactions.common.transaction import Transaction
|
||||
from .utils import flush_db
|
||||
from planetmint.config import Config
|
||||
|
||||
old_validator_instance = Validator()
|
||||
del old_validator_instance.models
|
||||
del old_validator_instance
|
||||
|
||||
conn = Connection()
|
||||
conn.close()
|
||||
conn.connect()
|
||||
|
||||
dbname = Config().get()["database"]["name"]
|
||||
flush_db(conn, dbname)
|
||||
|
||||
to_dict.cache_clear()
|
||||
from_dict.cache_clear()
|
||||
Transaction._input_valid.cache_clear()
|
||||
|
||||
validator = Validator()
|
||||
validator.models.connection.close()
|
||||
validator.models.connection.connect()
|
||||
@ -286,22 +346,6 @@ def eventqueue_fixture():
|
||||
return Queue()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def b_mock(b, network_validators):
|
||||
b.models.get_validators = mock_get_validators(network_validators)
|
||||
return b
|
||||
|
||||
|
||||
def mock_get_validators(network_validators):
|
||||
def validator_set(height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append({"public_key": {"type": "ed25519-base64", "value": public_key}, "voting_power": power})
|
||||
return validators
|
||||
|
||||
return validator_set
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def create_tx(alice, user_pk):
|
||||
from transactions.types.assets.create import Create
|
||||
@ -319,7 +363,10 @@ def signed_create_tx(alice, create_tx):
|
||||
@pytest.fixture
|
||||
def posted_create_tx(b, signed_create_tx, test_abci_rpc):
|
||||
res = test_abci_rpc.post_transaction(
|
||||
MODE_LIST, test_abci_rpc.tendermint_rpc_endpoint, signed_create_tx, BROADCAST_TX_COMMIT
|
||||
MODE_LIST,
|
||||
test_abci_rpc.tendermint_rpc_endpoint,
|
||||
signed_create_tx,
|
||||
BROADCAST_TX_COMMIT,
|
||||
)
|
||||
assert res.status_code == 200
|
||||
return signed_create_tx
|
||||
@ -356,7 +403,9 @@ def inputs(user_pk, b, alice):
|
||||
for height in range(1, 4):
|
||||
transactions = [
|
||||
Create.generate(
|
||||
[alice.public_key], [([user_pk], 1)], metadata=multihash(marshal({"data": f"{random.random()}"}))
|
||||
[alice.public_key],
|
||||
[([user_pk], 1)],
|
||||
metadata=multihash(marshal({"data": f"{random.random()}"})),
|
||||
).sign([alice.private_key])
|
||||
for _ in range(10)
|
||||
]
|
||||
@ -428,7 +477,13 @@ def _abci_http(request):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def abci_http(_setup_database, _configure_planetmint, abci_server, tendermint_host, tendermint_port):
|
||||
def abci_http(
|
||||
_setup_database,
|
||||
_configure_planetmint,
|
||||
abci_server,
|
||||
tendermint_host,
|
||||
tendermint_port,
|
||||
):
|
||||
import requests
|
||||
import time
|
||||
|
||||
@ -484,50 +539,6 @@ def wsserver_base_url(wsserver_scheme, wsserver_host, wsserver_port):
|
||||
return "{}://{}:{}".format(wsserver_scheme, wsserver_host, wsserver_port)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unspent_output_0():
|
||||
return {
|
||||
"amount": 1,
|
||||
"asset_id": "e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d",
|
||||
"condition_uri": "ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072",
|
||||
"fulfillment_message": '{"asset":{"data":{"hash":"06e47bcf9084f7ecfd2a2a2ad275444a"}},"id":"e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d","inputs":[{"fulfillment":"pGSAIIQT0Jm6LDlcSs9coJK4Q4W-SNtsO2EtMtQJ04EUjBMJgUAXKIqeaippbF-IClhhZNNaP6EIZ_OgrVQYU4mH6b-Vc3Tg-k6p-rJOlLGUUo_w8C5QgPHNRYFOqUk2f1q0Cs4G","fulfills":null,"owners_before":["9taLkHkaBXeSF8vrhDGFTAmcZuCEPqjQrKadfYGs4gHv"]}],"metadata":null,"operation":"CREATE","outputs":[{"amount":"1","condition":{"details":{"public_key":"6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz","type":"ed25519-sha-256"},"uri":"ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072"},"public_keys":["6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz"]},{"amount":"2","condition":{"details":{"public_key":"AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT","type":"ed25519-sha-256"},"uri":"ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072"},"public_keys":["AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT"]},{"amount":"3","condition":{"details":{"public_key":"HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB","type":"ed25519-sha-256"},"uri":"ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072"},"public_keys":["HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB"]}],"version":"1.0"}', # noqa: E501
|
||||
# noqa
|
||||
"output_index": 0,
|
||||
"transaction_id": "e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unspent_output_1():
|
||||
return {
|
||||
"amount": 2,
|
||||
"asset_id": "e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d",
|
||||
"condition_uri": "ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072",
|
||||
"fulfillment_message": '{"asset":{"data":{"hash":"06e47bcf9084f7ecfd2a2a2ad275444a"}},"id":"e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d","inputs":[{"fulfillment":"pGSAIIQT0Jm6LDlcSs9coJK4Q4W-SNtsO2EtMtQJ04EUjBMJgUAXKIqeaippbF-IClhhZNNaP6EIZ_OgrVQYU4mH6b-Vc3Tg-k6p-rJOlLGUUo_w8C5QgPHNRYFOqUk2f1q0Cs4G","fulfills":null,"owners_before":["9taLkHkaBXeSF8vrhDGFTAmcZuCEPqjQrKadfYGs4gHv"]}],"metadata":null,"operation":"CREATE","outputs":[{"amount":"1","condition":{"details":{"public_key":"6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz","type":"ed25519-sha-256"},"uri":"ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072"},"public_keys":["6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz"]},{"amount":"2","condition":{"details":{"public_key":"AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT","type":"ed25519-sha-256"},"uri":"ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072"},"public_keys":["AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT"]},{"amount":"3","condition":{"details":{"public_key":"HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB","type":"ed25519-sha-256"},"uri":"ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072"},"public_keys":["HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB"]}],"version":"1.0"}', # noqa: E501
|
||||
# noqa
|
||||
"output_index": 1,
|
||||
"transaction_id": "e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unspent_output_2():
|
||||
return {
|
||||
"amount": 3,
|
||||
"asset_id": "e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d",
|
||||
"condition_uri": "ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072",
|
||||
"fulfillment_message": '{"asset":{"data":{"hash":"06e47bcf9084f7ecfd2a2a2ad275444a"}},"id":"e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d","inputs":[{"fulfillment":"pGSAIIQT0Jm6LDlcSs9coJK4Q4W-SNtsO2EtMtQJ04EUjBMJgUAXKIqeaippbF-IClhhZNNaP6EIZ_OgrVQYU4mH6b-Vc3Tg-k6p-rJOlLGUUo_w8C5QgPHNRYFOqUk2f1q0Cs4G","fulfills":null,"owners_before":["9taLkHkaBXeSF8vrhDGFTAmcZuCEPqjQrKadfYGs4gHv"]}],"metadata":null,"operation":"CREATE","outputs":[{"amount":"1","condition":{"details":{"public_key":"6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz","type":"ed25519-sha-256"},"uri":"ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072"},"public_keys":["6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz"]},{"amount":"2","condition":{"details":{"public_key":"AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT","type":"ed25519-sha-256"},"uri":"ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072"},"public_keys":["AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT"]},{"amount":"3","condition":{"details":{"public_key":"HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB","type":"ed25519-sha-256"},"uri":"ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072"},"public_keys":["HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB"]}],"version":"1.0"}', # noqa: E501
|
||||
# noqa
|
||||
"output_index": 2,
|
||||
"transaction_id": "e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unspent_outputs(unspent_output_0, unspent_output_1, unspent_output_2):
|
||||
return unspent_output_0, unspent_output_1, unspent_output_2
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tarantool_client(db_context): # TODO Here add TarantoolConnectionClass
|
||||
return TarantoolDBConnection(host=db_context.host, port=db_context.port)
|
||||
@ -538,28 +549,6 @@ def utxo_collection(tarantool_client, _setup_database):
|
||||
return tarantool_client.get_space("utxos")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dummy_unspent_outputs():
|
||||
return [
|
||||
{"transaction_id": "a", "output_index": 0},
|
||||
{"transaction_id": "a", "output_index": 1},
|
||||
{"transaction_id": "b", "output_index": 0},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def utxoset(dummy_unspent_outputs, utxo_collection):
|
||||
from uuid import uuid4
|
||||
|
||||
num_rows_before_operation = utxo_collection.select().rowcount
|
||||
for utxo in dummy_unspent_outputs:
|
||||
res = utxo_collection.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
|
||||
assert res
|
||||
num_rows_after_operation = utxo_collection.select().rowcount
|
||||
assert num_rows_after_operation == num_rows_before_operation + 3
|
||||
return dummy_unspent_outputs, utxo_collection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def network_validators(node_keys):
|
||||
validator_pub_power = {}
|
||||
@ -698,19 +687,19 @@ def new_validator():
|
||||
node_id = "fake_node_id"
|
||||
|
||||
return [
|
||||
{"data": {"public_key": {"value": public_key, "type": "ed25519-base16"}, "power": power, "node_id": node_id}}
|
||||
{
|
||||
"data": {
|
||||
"public_key": {"value": public_key, "type": "ed25519-base16"},
|
||||
"power": power,
|
||||
"node_id": node_id,
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def valid_upsert_validator_election(b_mock, node_key, new_validator):
|
||||
voters = b_mock.get_recipients_list()
|
||||
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def valid_upsert_validator_election_2(b_mock, node_key, new_validator):
|
||||
voters = b_mock.get_recipients_list()
|
||||
def valid_upsert_validator_election(b, node_key, new_validator):
|
||||
voters = b.get_recipients_list()
|
||||
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
||||
|
||||
|
||||
@ -726,40 +715,6 @@ def ongoing_validator_election(b, valid_upsert_validator_election, ed25519_node_
|
||||
return valid_upsert_validator_election
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ongoing_validator_election_2(b, valid_upsert_validator_election_2, ed25519_node_keys):
|
||||
validators = b.models.get_validators(height=1)
|
||||
genesis_validators = {"validators": validators, "height": 0, "election_id": None}
|
||||
query.store_validator_set(b.models.connection, genesis_validators)
|
||||
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election_2])
|
||||
block_1 = Block(app_hash="hash_2", height=1, transactions=[valid_upsert_validator_election_2.id])
|
||||
b.models.store_block(block_1._asdict())
|
||||
return valid_upsert_validator_election_2
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def validator_election_votes(b_mock, ongoing_validator_election, ed25519_node_keys):
|
||||
voters = b_mock.get_recipients_list()
|
||||
votes = generate_votes(ongoing_validator_election, voters, ed25519_node_keys)
|
||||
return votes
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def validator_election_votes_2(b_mock, ongoing_validator_election_2, ed25519_node_keys):
|
||||
voters = b_mock.get_recipients_list()
|
||||
votes = generate_votes(ongoing_validator_election_2, voters, ed25519_node_keys)
|
||||
return votes
|
||||
|
||||
|
||||
def generate_votes(election, voters, keys):
|
||||
votes = []
|
||||
for voter, _ in enumerate(voters):
|
||||
v = gen_vote(election, voter, keys)
|
||||
votes.append(v)
|
||||
return votes
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def signed_2_0_create_tx():
|
||||
return {
|
||||
|
@ -7,10 +7,10 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
from base58 import b58decode
|
||||
from ipld import marshal, multihash
|
||||
from operator import attrgetter
|
||||
|
||||
from transactions.common import crypto
|
||||
from transactions.common.transaction import TransactionLink
|
||||
from transactions.common.transaction import Transaction
|
||||
from transactions.common.transaction import Transaction, TransactionLink, Input
|
||||
from transactions.types.assets.create import Create
|
||||
from transactions.types.assets.transfer import Transfer
|
||||
from planetmint.exceptions import CriticalDoubleSpend
|
||||
@ -64,7 +64,6 @@ class TestBigchainApi(object):
|
||||
def test_non_create_input_not_found(self, b, user_pk):
|
||||
from planetmint_cryptoconditions import Ed25519Sha256
|
||||
from transactions.common.exceptions import InputDoesNotExist
|
||||
from transactions.common.transaction import Input, TransactionLink
|
||||
|
||||
# Create an input for a non existing transaction
|
||||
input = Input(
|
||||
@ -104,14 +103,15 @@ class TestTransactionValidation(object):
|
||||
def test_non_create_valid_input_wrong_owner(self, b, user_pk):
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
from transactions.common.exceptions import InvalidSignature
|
||||
from transactions.common.transaction_link import TransactionLink
|
||||
|
||||
input_tx = b.models.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||
input_transaction = b.models.get_transaction(input_tx.txid)
|
||||
output = b.models.get_outputs_filtered(user_pk).pop()
|
||||
input_transaction = b.models.get_transaction(output.transaction_id)
|
||||
sk, pk = generate_key_pair()
|
||||
tx = Create.generate([pk], [([user_pk], 1)])
|
||||
tx.operation = "TRANSFER"
|
||||
tx.assets = [{"id": input_transaction.id}]
|
||||
tx.inputs[0].fulfills = input_tx
|
||||
tx.inputs[0].fulfills = TransactionLink(output.transaction_id, output.index)
|
||||
|
||||
with pytest.raises(InvalidSignature):
|
||||
b.validate_transaction(tx)
|
||||
@ -129,8 +129,8 @@ class TestTransactionValidation(object):
|
||||
class TestMultipleInputs(object):
|
||||
def test_transfer_single_owner_single_input(self, b, inputs, user_pk, user_sk):
|
||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||
tx_link = b.models.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||
input_tx = b.models.get_transaction(tx_link.txid)
|
||||
tx_output = b.models.get_outputs_filtered(user_pk).pop()
|
||||
input_tx = b.models.get_transaction(tx_output.transaction_id)
|
||||
tx_converted = Transaction.from_dict(input_tx.to_dict(), True)
|
||||
|
||||
tx = Transfer.generate(tx_converted.to_inputs(), [([user2_pk], 1)], asset_ids=[input_tx.id])
|
||||
@ -144,9 +144,9 @@ class TestMultipleInputs(object):
|
||||
def test_single_owner_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, inputs):
|
||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||
tx_link = b.models.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||
tx_output = b.models.get_outputs_filtered(user_pk).pop()
|
||||
|
||||
input_tx = b.models.get_transaction(tx_link.txid)
|
||||
input_tx = b.models.get_transaction(tx_output.transaction_id)
|
||||
tx_converted = Transaction.from_dict(input_tx.to_dict(), True)
|
||||
|
||||
tx = Transfer.generate(tx_converted.to_inputs(), [([user2_pk, user3_pk], 1)], asset_ids=[input_tx.id])
|
||||
@ -165,8 +165,8 @@ class TestMultipleInputs(object):
|
||||
tx = tx.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
owned_input = b.models.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||
input_tx = b.models.get_transaction(owned_input.txid)
|
||||
tx_output = b.models.get_outputs_filtered(user_pk).pop()
|
||||
input_tx = b.models.get_transaction(tx_output.transaction_id)
|
||||
input_tx_converted = Transaction.from_dict(input_tx.to_dict(), True)
|
||||
|
||||
transfer_tx = Transfer.generate(input_tx_converted.to_inputs(), [([user3_pk], 1)], asset_ids=[input_tx.id])
|
||||
@ -188,8 +188,8 @@ class TestMultipleInputs(object):
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
# get input
|
||||
tx_link = b.models.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||
tx_input = b.models.get_transaction(tx_link.txid)
|
||||
tx_output = b.models.get_outputs_filtered(user_pk).pop()
|
||||
tx_input = b.models.get_transaction(tx_output.transaction_id)
|
||||
input_tx_converted = Transaction.from_dict(tx_input.to_dict(), True)
|
||||
|
||||
tx = Transfer.generate(input_tx_converted.to_inputs(), [([user3_pk, user4_pk], 1)], asset_ids=[tx_input.id])
|
||||
@ -206,20 +206,24 @@ class TestMultipleInputs(object):
|
||||
tx = tx.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user2 = b.models.fastquery.get_outputs_by_public_key(user2_pk)
|
||||
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
|
||||
stored_tx = b.models.get_transaction(tx.id)
|
||||
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
owned_inputs_user2 = b.models.get_outputs_filtered(user2_pk)
|
||||
assert owned_inputs_user1 == [stored_tx.outputs[0]]
|
||||
assert owned_inputs_user2 == []
|
||||
|
||||
tx_transfer = Transfer.generate(tx.to_inputs(), [([user2_pk], 1)], asset_ids=[tx.id])
|
||||
tx_transfer = tx_transfer.sign([user_sk])
|
||||
b.models.store_bulk_transactions([tx_transfer])
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user2 = b.models.fastquery.get_outputs_by_public_key(user2_pk)
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
owned_inputs_user2 = b.models.get_outputs_filtered(user2_pk)
|
||||
|
||||
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
|
||||
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0)]
|
||||
stored_tx_transfer = b.models.get_transaction(tx_transfer.id)
|
||||
|
||||
assert owned_inputs_user1 == [stored_tx.outputs[0]]
|
||||
assert owned_inputs_user2 == [stored_tx_transfer.outputs[0]]
|
||||
|
||||
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
|
||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||
@ -230,11 +234,15 @@ class TestMultipleInputs(object):
|
||||
b.models.store_bulk_transactions([tx_create_signed])
|
||||
|
||||
# get input
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user2 = b.models.fastquery.get_outputs_by_public_key(user2_pk)
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
owned_inputs_user2 = b.models.get_outputs_filtered(user2_pk)
|
||||
|
||||
expected_owned_inputs_user1 = [TransactionLink(tx_create.id, 0), TransactionLink(tx_create.id, 1)]
|
||||
assert owned_inputs_user1 == expected_owned_inputs_user1
|
||||
stored_tx = b.models.get_transaction(tx_create.id)
|
||||
|
||||
expected_owned_inputs_user1 = [stored_tx.outputs[0], stored_tx.outputs[1]]
|
||||
assert sorted(owned_inputs_user1, key=attrgetter("index")) == sorted(
|
||||
expected_owned_inputs_user1, key=attrgetter("index")
|
||||
)
|
||||
assert owned_inputs_user2 == []
|
||||
|
||||
# transfer divisible asset divided in two outputs
|
||||
@ -243,11 +251,16 @@ class TestMultipleInputs(object):
|
||||
)
|
||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||
b.models.store_bulk_transactions([tx_transfer_signed])
|
||||
stored_tx_transfer = b.models.get_transaction(tx_transfer.id)
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user2 = b.models.fastquery.get_outputs_by_public_key(user2_pk)
|
||||
assert owned_inputs_user1 == expected_owned_inputs_user1
|
||||
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0), TransactionLink(tx_transfer.id, 1)]
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
owned_inputs_user2 = b.models.get_outputs_filtered(user2_pk)
|
||||
assert sorted(owned_inputs_user1, key=attrgetter("index")) == sorted(
|
||||
expected_owned_inputs_user1, key=attrgetter("index")
|
||||
)
|
||||
assert sorted(owned_inputs_user2, key=attrgetter("index")) == sorted(
|
||||
[stored_tx_transfer.outputs[0], stored_tx_transfer.outputs[1]], key=attrgetter("index")
|
||||
)
|
||||
|
||||
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk, alice):
|
||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||
@ -257,10 +270,11 @@ class TestMultipleInputs(object):
|
||||
tx = tx.sign([alice.private_key])
|
||||
|
||||
b.models.store_bulk_transactions([tx])
|
||||
stored_tx = b.models.get_transaction(tx.id)
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user2 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
expected_owned_inputs_user1 = [TransactionLink(tx.id, 0)]
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
owned_inputs_user2 = b.models.get_outputs_filtered(user_pk)
|
||||
expected_owned_inputs_user1 = [stored_tx.outputs[0]]
|
||||
|
||||
assert owned_inputs_user1 == owned_inputs_user2
|
||||
assert owned_inputs_user1 == expected_owned_inputs_user1
|
||||
@ -269,9 +283,9 @@ class TestMultipleInputs(object):
|
||||
tx = tx.sign([user_sk, user2_sk])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user2 = b.models.fastquery.get_outputs_by_public_key(user2_pk)
|
||||
spent_user1 = b.models.get_spent(tx.id, 0)
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
owned_inputs_user2 = b.models.get_outputs_filtered(user2_pk)
|
||||
spent_user1 = b.models.get_spending_transaction(tx.id, 0)
|
||||
|
||||
assert owned_inputs_user1 == owned_inputs_user2
|
||||
assert not spent_user1
|
||||
@ -283,11 +297,11 @@ class TestMultipleInputs(object):
|
||||
tx = tx.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk).pop()
|
||||
|
||||
# check spents
|
||||
input_txid = owned_inputs_user1.txid
|
||||
spent_inputs_user1 = b.models.get_spent(input_txid, 0)
|
||||
input_txid = owned_inputs_user1.transaction_id
|
||||
spent_inputs_user1 = b.models.get_spending_transaction(input_txid, 0)
|
||||
assert spent_inputs_user1 is None
|
||||
|
||||
# create a transaction and send it
|
||||
@ -295,7 +309,7 @@ class TestMultipleInputs(object):
|
||||
tx = tx.sign([user_sk])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
spent_inputs_user1 = b.models.get_spent(input_txid, 0)
|
||||
spent_inputs_user1 = b.models.get_spending_transaction(input_txid, 0)
|
||||
assert spent_inputs_user1 == tx.to_dict()
|
||||
|
||||
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
|
||||
@ -307,11 +321,11 @@ class TestMultipleInputs(object):
|
||||
tx_create_signed = tx_create.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx_create_signed])
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
|
||||
# check spents
|
||||
for input_tx in owned_inputs_user1:
|
||||
assert b.models.get_spent(input_tx.txid, input_tx.output) is None
|
||||
assert b.models.get_spending_transaction(input_tx.transaction_id, input_tx.index) is None
|
||||
|
||||
# transfer the first 2 inputs
|
||||
tx_transfer = Transfer.generate(
|
||||
@ -322,12 +336,12 @@ class TestMultipleInputs(object):
|
||||
|
||||
# check that used inputs are marked as spent
|
||||
for ffill in tx_create.to_inputs()[:2]:
|
||||
spent_tx = b.models.get_spent(ffill.fulfills.txid, ffill.fulfills.output)
|
||||
spent_tx = b.models.get_spending_transaction(ffill.fulfills.txid, ffill.fulfills.output)
|
||||
assert spent_tx == tx_transfer_signed.to_dict()
|
||||
|
||||
# check if remaining transaction that was unspent is also perceived
|
||||
# spendable by Planetmint
|
||||
assert b.models.get_spent(tx_create.to_inputs()[2].fulfills.txid, 2) is None
|
||||
assert b.models.get_spending_transaction(tx_create.to_inputs()[2].fulfills.txid, 2) is None
|
||||
|
||||
def test_get_spent_multiple_owners(self, b, user_sk, user_pk, alice):
|
||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||
@ -342,10 +356,10 @@ class TestMultipleInputs(object):
|
||||
|
||||
b.models.store_bulk_transactions(transactions)
|
||||
|
||||
owned_inputs_user1 = b.models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
owned_inputs_user1 = b.models.get_outputs_filtered(user_pk)
|
||||
# check spents
|
||||
for input_tx in owned_inputs_user1:
|
||||
assert b.models.get_spent(input_tx.txid, input_tx.output) is None
|
||||
assert b.models.get_spending_transaction(input_tx.transaction_id, input_tx.index) is None
|
||||
|
||||
# create a transaction
|
||||
tx = Transfer.generate(transactions[0].to_inputs(), [([user3_pk], 1)], asset_ids=[transactions[0].id])
|
||||
@ -353,59 +367,49 @@ class TestMultipleInputs(object):
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
# check that used inputs are marked as spent
|
||||
assert b.models.get_spent(transactions[0].id, 0) == tx.to_dict()
|
||||
assert b.models.get_spending_transaction(transactions[0].id, 0) == tx.to_dict()
|
||||
# check that the other remain marked as unspent
|
||||
for unspent in transactions[1:]:
|
||||
assert b.models.get_spent(unspent.id, 0) is None
|
||||
assert b.models.get_spending_transaction(unspent.id, 0) is None
|
||||
|
||||
|
||||
def test_get_outputs_filtered_only_unspent(b):
|
||||
from transactions.common.transaction import TransactionLink
|
||||
def test_get_outputs_filtered_only_unspent(b, alice):
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1), ([alice.public_key], 1)])
|
||||
tx = tx.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
go = "planetmint.model.fastquery.FastQuery.get_outputs_by_public_key"
|
||||
with patch(go) as get_outputs:
|
||||
get_outputs.return_value = [TransactionLink("a", 1), TransactionLink("b", 2)]
|
||||
fs = "planetmint.model.fastquery.FastQuery.filter_spent_outputs"
|
||||
with patch(fs) as filter_spent:
|
||||
filter_spent.return_value = [TransactionLink("b", 2)]
|
||||
out = b.models.get_outputs_filtered("abc", spent=False)
|
||||
get_outputs.assert_called_once_with("abc")
|
||||
assert out == [TransactionLink("b", 2)]
|
||||
tx_transfer = Transfer.generate(tx.to_inputs([0]), [([alice.public_key], 1)], asset_ids=[tx.id])
|
||||
tx_transfer = tx_transfer.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx_transfer])
|
||||
|
||||
outputs = b.models.get_outputs_filtered(alice.public_key, spent=False)
|
||||
assert len(outputs) == 2
|
||||
|
||||
|
||||
def test_get_outputs_filtered_only_spent(b):
|
||||
from transactions.common.transaction import TransactionLink
|
||||
def test_get_outputs_filtered_only_spent(b, alice):
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1), ([alice.public_key], 1)])
|
||||
tx = tx.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
go = "planetmint.model.fastquery.FastQuery.get_outputs_by_public_key"
|
||||
with patch(go) as get_outputs:
|
||||
get_outputs.return_value = [TransactionLink("a", 1), TransactionLink("b", 2)]
|
||||
fs = "planetmint.model.fastquery.FastQuery.filter_unspent_outputs"
|
||||
with patch(fs) as filter_spent:
|
||||
filter_spent.return_value = [TransactionLink("b", 2)]
|
||||
out = b.models.get_outputs_filtered("abc", spent=True)
|
||||
get_outputs.assert_called_once_with("abc")
|
||||
assert out == [TransactionLink("b", 2)]
|
||||
tx_transfer = Transfer.generate(tx.to_inputs([0]), [([alice.public_key], 1)], asset_ids=[tx.id])
|
||||
tx_transfer = tx_transfer.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx_transfer])
|
||||
|
||||
outputs = b.models.get_outputs_filtered(alice.public_key, spent=True)
|
||||
assert len(outputs) == 1
|
||||
|
||||
|
||||
# @patch("planetmint.model.fastquery.FastQuery.filter_unspent_outputs")
|
||||
# @patch("planetmint.model.fastquery.FastQuery.filter_spent_outputs")
|
||||
def test_get_outputs_filtered(
|
||||
b,
|
||||
mocker,
|
||||
):
|
||||
from transactions.common.transaction import TransactionLink
|
||||
def test_get_outputs_filtered(b, alice):
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1), ([alice.public_key], 1)])
|
||||
tx = tx.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
mock_filter_spent_outputs = mocker.patch("planetmint.model.fastquery.FastQuery.filter_spent_outputs")
|
||||
mock_filter_unspent_outputs = mocker.patch("planetmint.model.fastquery.FastQuery.filter_unspent_outputs")
|
||||
tx_transfer = Transfer.generate(tx.to_inputs([0]), [([alice.public_key], 1)], asset_ids=[tx.id])
|
||||
tx_transfer = tx_transfer.sign([alice.private_key])
|
||||
b.models.store_bulk_transactions([tx_transfer])
|
||||
|
||||
go = "planetmint.model.fastquery.FastQuery.get_outputs_by_public_key"
|
||||
with patch(go) as get_outputs:
|
||||
get_outputs.return_value = [TransactionLink("a", 1), TransactionLink("b", 2)]
|
||||
out = b.models.get_outputs_filtered("abc")
|
||||
get_outputs.assert_called_once_with("abc")
|
||||
mock_filter_spent_outputs.assert_not_called()
|
||||
mock_filter_unspent_outputs.assert_not_called()
|
||||
assert out == get_outputs.return_value
|
||||
outputs = b.models.get_outputs_filtered(alice.public_key)
|
||||
assert len(outputs) == 3
|
||||
|
||||
|
||||
def test_cant_spend_same_input_twice_in_tx(b, alice):
|
||||
|
@ -5,10 +5,14 @@ from planetmint.abci.block import Block
|
||||
from transactions.types.elections.election import Election
|
||||
from transactions.types.elections.chain_migration_election import ChainMigrationElection
|
||||
from transactions.types.elections.validator_election import ValidatorElection
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_process_block_concludes_all_elections(b):
|
||||
del b.models
|
||||
b.models = DataAccessor()
|
||||
b.models.connect()
|
||||
validators = generate_validators([1] * 4)
|
||||
b.models.store_validator_set(1, [v["storage"] for v in validators])
|
||||
|
||||
|
@ -1,9 +1,28 @@
|
||||
import pytest
|
||||
from transactions.types.elections.chain_migration_election import ChainMigrationElection
|
||||
|
||||
|
||||
def test_valid_migration_election(b_mock, node_key):
|
||||
voters = b_mock.get_recipients_list()
|
||||
@pytest.mark.bdb
|
||||
def test_valid_migration_election(monkeypatch, b, node_key, network_validators):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
voters = b.get_recipients_list()
|
||||
election = ChainMigrationElection.generate([node_key.public_key], voters, [{"data": {}}], None).sign(
|
||||
[node_key.private_key]
|
||||
)
|
||||
assert b_mock.validate_election(election)
|
||||
assert b.validate_election(election)
|
||||
m.undo()
|
||||
|
@ -46,6 +46,7 @@ def generate_init_chain_request(chain_id, vals=None):
|
||||
return types.RequestInitChain(validators=vals, chain_id=chain_id)
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_init_chain_successfully_registers_chain(b):
|
||||
request = generate_init_chain_request("chain-XYZ")
|
||||
res = ApplicationLogic(validator=b).init_chain(request)
|
||||
|
@ -1,134 +0,0 @@
|
||||
# Copyright © 2020 Interplanetary Database Association e.V.,
|
||||
# Planetmint and IPDB software contributors.
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
|
||||
import pytest
|
||||
|
||||
from transactions.common.transaction import TransactionLink
|
||||
from transactions.types.assets.create import Create
|
||||
from transactions.types.assets.transfer import Transfer
|
||||
|
||||
pytestmark = pytest.mark.bdb
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def txns(b, user_pk, user_sk, user2_pk, user2_sk, test_models):
|
||||
txs = [
|
||||
Create.generate([user_pk], [([user2_pk], 1)]).sign([user_sk]),
|
||||
Create.generate([user2_pk], [([user_pk], 1)]).sign([user2_sk]),
|
||||
Create.generate([user_pk], [([user_pk], 1), ([user2_pk], 1)]).sign([user_sk]),
|
||||
]
|
||||
b.models.store_bulk_transactions(txs)
|
||||
return txs
|
||||
|
||||
|
||||
def test_get_outputs_by_public_key(b, user_pk, user2_pk, txns, test_models):
|
||||
expected = [TransactionLink(txns[1].id, 0), TransactionLink(txns[2].id, 0)]
|
||||
actual = test_models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
|
||||
_all_txs = set([tx.txid for tx in expected + actual])
|
||||
assert len(_all_txs) == 2
|
||||
# assert b.models.fastquery.get_outputs_by_public_key(user_pk) == [ # OLD VERIFICATION
|
||||
# TransactionLink(txns[1].id, 0),
|
||||
# TransactionLink(txns[2].id, 0)
|
||||
# ]
|
||||
actual_1 = test_models.fastquery.get_outputs_by_public_key(user2_pk)
|
||||
expected_1 = [
|
||||
TransactionLink(txns[0].id, 0),
|
||||
TransactionLink(txns[2].id, 1),
|
||||
]
|
||||
_all_tx_1 = set([tx.txid for tx in actual_1 + expected_1])
|
||||
assert len(_all_tx_1) == 2
|
||||
# assert b.models.fastquery.get_outputs_by_public_key(user2_pk) == [ # OLD VERIFICATION
|
||||
# TransactionLink(txns[0].id, 0),
|
||||
# TransactionLink(txns[2].id, 1),
|
||||
# ]
|
||||
|
||||
|
||||
def test_filter_spent_outputs(b, user_pk, user_sk, test_models):
|
||||
out = [([user_pk], 1)]
|
||||
tx1 = Create.generate([user_pk], out * 2)
|
||||
tx1.sign([user_sk])
|
||||
|
||||
inputs = tx1.to_inputs()
|
||||
|
||||
tx2 = Transfer.generate([inputs[0]], out, [tx1.id])
|
||||
tx2.sign([user_sk])
|
||||
|
||||
# tx2 produces a new unspent. inputs[1] remains unspent.
|
||||
b.models.store_bulk_transactions([tx1, tx2])
|
||||
|
||||
outputs = test_models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
unspents = test_models.fastquery.filter_spent_outputs(outputs)
|
||||
|
||||
assert set(unsp for unsp in unspents) == {
|
||||
inputs[1].fulfills,
|
||||
tx2.to_inputs()[0].fulfills,
|
||||
}
|
||||
|
||||
|
||||
def test_filter_unspent_outputs(b, user_pk, user_sk, test_models):
|
||||
out = [([user_pk], 1)]
|
||||
tx1 = Create.generate([user_pk], out * 2)
|
||||
tx1.sign([user_sk])
|
||||
|
||||
inputs = tx1.to_inputs()
|
||||
|
||||
tx2 = Transfer.generate([inputs[0]], out, [tx1.id])
|
||||
tx2.sign([user_sk])
|
||||
|
||||
# tx2 produces a new unspent. input[1] remains unspent.
|
||||
b.models.store_bulk_transactions([tx1, tx2])
|
||||
|
||||
outputs = test_models.fastquery.get_outputs_by_public_key(user_pk)
|
||||
spents = test_models.fastquery.filter_unspent_outputs(outputs)
|
||||
|
||||
assert set(sp for sp in spents) == {
|
||||
inputs[0].fulfills,
|
||||
}
|
||||
|
||||
|
||||
def test_outputs_query_key_order(b, user_pk, user_sk, user2_pk, user2_sk, test_models, test_validator):
|
||||
from planetmint import backend
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.backend import query
|
||||
|
||||
tx1 = Create.generate([user_pk], [([user_pk], 3), ([user_pk], 2), ([user_pk], 1)]).sign([user_sk])
|
||||
b.models.store_bulk_transactions([tx1])
|
||||
|
||||
inputs = tx1.to_inputs()
|
||||
tx2 = Transfer.generate([inputs[1]], [([user2_pk], 2)], [tx1.id]).sign([user_sk])
|
||||
assert test_validator.validate_transaction(tx2)
|
||||
|
||||
tx2_dict = tx2.to_dict()
|
||||
fulfills = tx2_dict["inputs"][0]["fulfills"]
|
||||
tx2_dict["inputs"][0]["fulfills"] = {
|
||||
"transaction_id": fulfills["transaction_id"],
|
||||
"output_index": fulfills["output_index"],
|
||||
}
|
||||
backend.query.store_transactions(test_models.connection, [tx2_dict])
|
||||
|
||||
outputs = test_models.get_outputs_filtered(user_pk, spent=False)
|
||||
assert len(outputs) == 2
|
||||
|
||||
outputs = test_models.get_outputs_filtered(user2_pk, spent=False)
|
||||
assert len(outputs) == 1
|
||||
|
||||
# clean the transaction, metdata and asset collection
|
||||
connection = Connection()
|
||||
query.delete_transactions(test_models.connection, txn_ids=[tx1.id, tx2.id])
|
||||
|
||||
b.models.store_bulk_transactions([tx1])
|
||||
tx2_dict = tx2.to_dict()
|
||||
tx2_dict["inputs"][0]["fulfills"] = {
|
||||
"output_index": fulfills["output_index"],
|
||||
"transaction_id": fulfills["transaction_id"],
|
||||
}
|
||||
|
||||
backend.query.store_transactions(test_models.connection, [tx2_dict])
|
||||
outputs = test_models.get_outputs_filtered(user_pk, spent=False)
|
||||
assert len(outputs) == 2
|
||||
|
||||
outputs = test_models.get_outputs_filtered(user2_pk, spent=False)
|
||||
assert len(outputs) == 1
|
@ -22,7 +22,6 @@ from ipld import marshal, multihash
|
||||
from uuid import uuid4
|
||||
|
||||
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
|
||||
from tests.utils import delete_unspent_outputs, get_utxoset_merkle_root, store_unspent_outputs, update_utxoset
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@ -152,17 +151,17 @@ def test_post_transaction_invalid_mode(b, test_abci_rpc):
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_update_utxoset(b, signed_create_tx, signed_transfer_tx, db_conn):
|
||||
update_utxoset(b.models.connection, signed_create_tx)
|
||||
b.models.update_utxoset(signed_create_tx.to_dict())
|
||||
utxoset = db_conn.get_space("utxos")
|
||||
assert utxoset.select().rowcount == 1
|
||||
utxo = utxoset.select().data
|
||||
assert utxo[0][1] == signed_create_tx.id
|
||||
assert utxo[0][2] == 0
|
||||
update_utxoset(b.models.connection, signed_transfer_tx)
|
||||
assert utxo[0][5] == signed_create_tx.id
|
||||
assert utxo[0][4] == 0
|
||||
b.models.update_utxoset(signed_transfer_tx.to_dict())
|
||||
assert utxoset.select().rowcount == 1
|
||||
utxo = utxoset.select().data
|
||||
assert utxo[0][1] == signed_transfer_tx.id
|
||||
assert utxo[0][2] == 0
|
||||
assert utxo[0][5] == signed_transfer_tx.id
|
||||
assert utxo[0][4] == 0
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@ -184,107 +183,80 @@ def test_store_bulk_transaction(mocker, b, signed_create_tx, signed_transfer_tx)
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_delete_zero_unspent_outputs(b, utxoset):
|
||||
unspent_outputs, utxo_collection = utxoset
|
||||
num_rows_before_operation = utxo_collection.select().rowcount
|
||||
delete_res = delete_unspent_outputs(b.models.connection) # noqa: F841
|
||||
num_rows_after_operation = utxo_collection.select().rowcount
|
||||
# assert delete_res is None
|
||||
def test_delete_zero_unspent_outputs(b, alice):
|
||||
from planetmint.backend.tarantool.sync_io import query
|
||||
|
||||
utxo_space = b.models.connection.get_space("utxos")
|
||||
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 8), ([alice.public_key], 1)]).sign(
|
||||
[alice.private_key]
|
||||
)
|
||||
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
num_rows_before_operation = utxo_space.select().rowcount
|
||||
query.delete_unspent_outputs(b.models.connection, []) # noqa: F841
|
||||
num_rows_after_operation = utxo_space.select().rowcount
|
||||
assert num_rows_before_operation == num_rows_after_operation
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_delete_one_unspent_outputs(b, dummy_unspent_outputs):
|
||||
def test_delete_one_unspent_outputs(b, alice):
|
||||
from planetmint.backend.tarantool.sync_io import query
|
||||
|
||||
utxo_space = b.models.connection.get_space("utxos")
|
||||
for utxo in dummy_unspent_outputs:
|
||||
res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
|
||||
assert res
|
||||
|
||||
delete_unspent_outputs(b.models.connection, dummy_unspent_outputs[0])
|
||||
res1 = utxo_space.select(["a", 1], index="utxo_by_transaction_id_and_output_index").data
|
||||
res2 = utxo_space.select(["b", 0], index="utxo_by_transaction_id_and_output_index").data
|
||||
assert len(res1) + len(res2) == 2
|
||||
res3 = utxo_space.select(["a", 0], index="utxo_by_transaction_id_and_output_index").data
|
||||
assert len(res3) == 0
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 8), ([alice.public_key], 1)]).sign(
|
||||
[alice.private_key]
|
||||
)
|
||||
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
query.delete_unspent_outputs(b.models.connection, [{"transaction_id": tx.id, "output_index": 0}])
|
||||
res1 = utxo_space.select([tx.id, 1], index="utxo_by_transaction_id_and_output_index").data
|
||||
res2 = utxo_space.select([tx.id, 0], index="utxo_by_transaction_id_and_output_index").data
|
||||
assert len(res1) + len(res2) == 1
|
||||
assert len(res2) == 0
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_delete_many_unspent_outputs(b, dummy_unspent_outputs):
|
||||
def test_delete_many_unspent_outputs(b, alice):
|
||||
from planetmint.backend.tarantool.sync_io import query
|
||||
|
||||
utxo_space = b.models.connection.get_space("utxos")
|
||||
for utxo in dummy_unspent_outputs:
|
||||
res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
|
||||
assert res
|
||||
|
||||
delete_unspent_outputs(b.models.connection, *dummy_unspent_outputs[::2])
|
||||
res1 = utxo_space.select(["a", 0], index="utxo_by_transaction_id_and_output_index").data
|
||||
res2 = utxo_space.select(["b", 0], index="utxo_by_transaction_id_and_output_index").data
|
||||
assert len(res1) + len(res2) == 0
|
||||
res3 = utxo_space.select([], index="utxo_by_transaction_id_and_output_index").data
|
||||
assert len(res3) == 1
|
||||
tx = Create.generate(
|
||||
[alice.public_key], [([alice.public_key], 8), ([alice.public_key], 1), ([alice.public_key], 4)]
|
||||
).sign([alice.private_key])
|
||||
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_store_zero_unspent_output(b):
|
||||
utxos = b.models.connection.get_space("utxos")
|
||||
num_rows_before_operation = utxos.select().rowcount
|
||||
res = store_unspent_outputs(b.models.connection)
|
||||
num_rows_after_operation = utxos.select().rowcount
|
||||
assert res is None
|
||||
assert num_rows_before_operation == num_rows_after_operation
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_store_one_unspent_output(b, unspent_output_1, utxo_collection):
|
||||
from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
|
||||
|
||||
res = store_unspent_outputs(b.models.connection, unspent_output_1)
|
||||
if not isinstance(b.models.connection, TarantoolDBConnection):
|
||||
assert res.acknowledged
|
||||
assert len(list(res)) == 1
|
||||
assert (
|
||||
utxo_collection.count_documents(
|
||||
{
|
||||
"transaction_id": unspent_output_1["transaction_id"],
|
||||
"output_index": unspent_output_1["output_index"],
|
||||
}
|
||||
query.delete_unspent_outputs(
|
||||
b.models.connection,
|
||||
[{"transaction_id": tx.id, "output_index": 0}, {"transaction_id": tx.id, "output_index": 2}],
|
||||
)
|
||||
== 1
|
||||
)
|
||||
else:
|
||||
utx_space = b.models.connection.get_space("utxos")
|
||||
res = utx_space.select(
|
||||
[unspent_output_1["transaction_id"], unspent_output_1["output_index"]],
|
||||
index="utxo_by_transaction_id_and_output_index",
|
||||
)
|
||||
assert len(res.data) == 1
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_store_many_unspent_outputs(b, unspent_outputs):
|
||||
store_unspent_outputs(b.models.connection, *unspent_outputs)
|
||||
utxo_space = b.models.connection.get_space("utxos")
|
||||
res = utxo_space.select([unspent_outputs[0]["transaction_id"]], index="utxos_by_transaction_id")
|
||||
assert len(res.data) == 3
|
||||
res1 = utxo_space.select([tx.id, 1], index="utxo_by_transaction_id_and_output_index").data
|
||||
res2 = utxo_space.select([tx.id, 0], index="utxo_by_transaction_id_and_output_index").data
|
||||
assert len(res1) + len(res2) == 1
|
||||
|
||||
|
||||
def test_get_utxoset_merkle_root_when_no_utxo(b):
|
||||
assert get_utxoset_merkle_root(b.models.connection) == sha3_256(b"").hexdigest()
|
||||
assert b.models.get_utxoset_merkle_root() == sha3_256(b"").hexdigest()
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_get_utxoset_merkle_root(b, dummy_unspent_outputs):
|
||||
utxo_space = b.models.connection.get_space("utxos")
|
||||
for utxo in dummy_unspent_outputs:
|
||||
res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
|
||||
assert res
|
||||
def test_get_utxoset_merkle_root(b, user_sk, user_pk):
|
||||
tx = Create.generate([user_pk], [([user_pk], 8), ([user_pk], 1), ([user_pk], 4)]).sign([user_sk])
|
||||
|
||||
expected_merkle_root = "86d311c03115bf4d287f8449ca5828505432d69b82762d47077b1c00fe426eac"
|
||||
merkle_root = get_utxoset_merkle_root(b.models.connection)
|
||||
assert merkle_root == expected_merkle_root
|
||||
b.models.store_bulk_transactions([tx])
|
||||
|
||||
expected_merkle_root = "e5fce6fed606b72744330b28b2f6d68f2eca570c4cf8e3c418b0c3150c75bfe2"
|
||||
merkle_root = b.models.get_utxoset_merkle_root()
|
||||
assert merkle_root in expected_merkle_root
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_get_spent_transaction_double_spend(b, alice, bob, carol):
|
||||
def test_get_spending_transaction_double_spend(b, alice, bob, carol):
|
||||
from transactions.common.exceptions import DoubleSpend
|
||||
|
||||
assets = [{"data": multihash(marshal({"test": "asset"}))}]
|
||||
@ -308,15 +280,15 @@ def test_get_spent_transaction_double_spend(b, alice, bob, carol):
|
||||
with pytest.raises(DoubleSpend):
|
||||
b.validate_transaction(same_input_double_spend)
|
||||
|
||||
assert b.models.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output, [tx_transfer])
|
||||
assert b.models.get_spending_transaction(tx.id, tx_transfer.inputs[0].fulfills.output, [tx_transfer])
|
||||
|
||||
with pytest.raises(DoubleSpend):
|
||||
b.models.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output, [tx_transfer, double_spend])
|
||||
b.models.get_spending_transaction(tx.id, tx_transfer.inputs[0].fulfills.output, [tx_transfer, double_spend])
|
||||
|
||||
b.models.store_bulk_transactions([tx_transfer])
|
||||
|
||||
with pytest.raises(DoubleSpend):
|
||||
b.models.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output, [double_spend])
|
||||
b.models.get_spending_transaction(tx.id, tx_transfer.inputs[0].fulfills.output, [double_spend])
|
||||
|
||||
|
||||
def test_validation_with_transaction_buffer(b):
|
||||
|
@ -48,7 +48,7 @@ def test_bigchain_class_default_initialization(config):
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_get_spent_issue_1271(b, alice, bob, carol):
|
||||
def test_get_spending_transaction_issue_1271(b, alice, bob, carol):
|
||||
tx_1 = Create.generate(
|
||||
[carol.public_key],
|
||||
[([carol.public_key], 8)],
|
||||
@ -88,7 +88,7 @@ def test_get_spent_issue_1271(b, alice, bob, carol):
|
||||
assert b.validate_transaction(tx_5)
|
||||
|
||||
b.models.store_bulk_transactions([tx_5])
|
||||
assert b.models.get_spent(tx_2.id, 0) == tx_5.to_dict()
|
||||
assert not b.models.get_spent(tx_5.id, 0)
|
||||
assert b.models.get_spending_transaction(tx_2.id, 0) == tx_5.to_dict()
|
||||
assert not b.models.get_spending_transaction(tx_5.id, 0)
|
||||
assert b.models.get_outputs_filtered(alice.public_key)
|
||||
assert b.models.get_outputs_filtered(alice.public_key, spent=False)
|
||||
|
@ -11,15 +11,15 @@ from transactions.types.elections.validator_election import ValidatorElection
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def valid_upsert_validator_election_b(b, node_key, new_validator):
|
||||
def valid_upsert_validator_election(b, node_key, new_validator):
|
||||
voters = b.get_recipients_list()
|
||||
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@patch("transactions.types.elections.election.uuid4", lambda: "mock_uuid4")
|
||||
def fixed_seed_election(b_mock, node_key, new_validator):
|
||||
voters = b_mock.get_recipients_list()
|
||||
def fixed_seed_election(b, node_key, new_validator):
|
||||
voters = b.get_recipients_list()
|
||||
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
import pytest
|
||||
import codecs
|
||||
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
from planetmint.abci.rpc import MODE_LIST, MODE_COMMIT
|
||||
from planetmint.abci.utils import public_key_to_base64
|
||||
|
||||
@ -22,51 +23,105 @@ from tests.utils import generate_block, gen_vote
|
||||
pytestmark = [pytest.mark.execute]
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_upsert_validator_valid_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys):
|
||||
b_mock.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
# helper
|
||||
def get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator):
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
voters = b.get_recipients_list()
|
||||
valid_upsert_validator_election = ValidatorElection.generate(
|
||||
[node_key.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key])
|
||||
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
return valid_upsert_validator_election
|
||||
|
||||
|
||||
# helper
|
||||
def get_voting_set(valid_upsert_validator_election, ed25519_node_keys):
|
||||
input0 = valid_upsert_validator_election.to_inputs()[0]
|
||||
votes = valid_upsert_validator_election.outputs[0].amount
|
||||
public_key0 = input0.owners_before[0]
|
||||
key0 = ed25519_node_keys[public_key0]
|
||||
return input0, votes, key0
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_upsert_validator_valid_election_vote(
|
||||
monkeypatch, b, network_validators, new_validator, node_key, ed25519_node_keys
|
||||
):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator)
|
||||
input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys)
|
||||
|
||||
election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id)
|
||||
|
||||
vote = Vote.generate(
|
||||
[input0], [([election_pub_key], votes)], election_ids=[valid_upsert_validator_election.id]
|
||||
).sign([key0.private_key])
|
||||
assert b_mock.validate_transaction(vote)
|
||||
assert b.validate_transaction(vote)
|
||||
m.undo()
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_upsert_validator_valid_non_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys):
|
||||
b_mock.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
def test_upsert_validator_valid_non_election_vote(
|
||||
monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys
|
||||
):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
input0 = valid_upsert_validator_election.to_inputs()[0]
|
||||
votes = valid_upsert_validator_election.outputs[0].amount
|
||||
public_key0 = input0.owners_before[0]
|
||||
key0 = ed25519_node_keys[public_key0]
|
||||
with monkeypatch.context() as m:
|
||||
valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator)
|
||||
input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys)
|
||||
|
||||
election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id)
|
||||
|
||||
# Ensure that threshold conditions are now allowed
|
||||
with pytest.raises(ValidationError):
|
||||
Vote.generate(
|
||||
[input0], [([election_pub_key, key0.public_key], votes)], election_ids=[valid_upsert_validator_election.id]
|
||||
[input0],
|
||||
[([election_pub_key, key0.public_key], votes)],
|
||||
election_ids=[valid_upsert_validator_election.id],
|
||||
).sign([key0.private_key])
|
||||
m.undo()
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_upsert_validator_delegate_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys):
|
||||
def test_upsert_validator_delegate_election_vote(
|
||||
monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys
|
||||
):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator)
|
||||
alice = generate_key_pair()
|
||||
|
||||
b_mock.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
|
||||
input0 = valid_upsert_validator_election.to_inputs()[0]
|
||||
votes = valid_upsert_validator_election.outputs[0].amount
|
||||
public_key0 = input0.owners_before[0]
|
||||
key0 = ed25519_node_keys[public_key0]
|
||||
input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys)
|
||||
|
||||
delegate_vote = Vote.generate(
|
||||
[input0],
|
||||
@ -74,32 +129,43 @@ def test_upsert_validator_delegate_election_vote(b_mock, valid_upsert_validator_
|
||||
election_ids=[valid_upsert_validator_election.id],
|
||||
).sign([key0.private_key])
|
||||
|
||||
assert b_mock.validate_transaction(delegate_vote)
|
||||
assert b.validate_transaction(delegate_vote)
|
||||
|
||||
b_mock.models.store_bulk_transactions([delegate_vote])
|
||||
b.models.store_bulk_transactions([delegate_vote])
|
||||
election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id)
|
||||
|
||||
alice_votes = delegate_vote.to_inputs()[0]
|
||||
alice_casted_vote = Vote.generate(
|
||||
[alice_votes], [([election_pub_key], 3)], election_ids=[valid_upsert_validator_election.id]
|
||||
).sign([alice.private_key])
|
||||
assert b_mock.validate_transaction(alice_casted_vote)
|
||||
assert b.validate_transaction(alice_casted_vote)
|
||||
|
||||
key0_votes = delegate_vote.to_inputs()[1]
|
||||
key0_casted_vote = Vote.generate(
|
||||
[key0_votes], [([election_pub_key], votes - 3)], election_ids=[valid_upsert_validator_election.id]
|
||||
).sign([key0.private_key])
|
||||
assert b_mock.validate_transaction(key0_casted_vote)
|
||||
assert b.validate_transaction(key0_casted_vote)
|
||||
m.undo()
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_upsert_validator_invalid_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys):
|
||||
b_mock.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
def test_upsert_validator_invalid_election_vote(
|
||||
monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys
|
||||
):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
input0 = valid_upsert_validator_election.to_inputs()[0]
|
||||
votes = valid_upsert_validator_election.outputs[0].amount
|
||||
public_key0 = input0.owners_before[0]
|
||||
key0 = ed25519_node_keys[public_key0]
|
||||
with monkeypatch.context() as m:
|
||||
valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator)
|
||||
input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys)
|
||||
|
||||
election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id)
|
||||
|
||||
@ -108,19 +174,28 @@ def test_upsert_validator_invalid_election_vote(b_mock, valid_upsert_validator_e
|
||||
).sign([key0.private_key])
|
||||
|
||||
with pytest.raises(AmountError):
|
||||
assert b_mock.validate_transaction(vote)
|
||||
assert b.validate_transaction(vote)
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_valid_election_votes_received(b_mock, valid_upsert_validator_election, ed25519_node_keys):
|
||||
alice = generate_key_pair()
|
||||
b_mock.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
assert b_mock.get_commited_votes(valid_upsert_validator_election) == 0
|
||||
def test_valid_election_votes_received(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
input0 = valid_upsert_validator_election.to_inputs()[0]
|
||||
votes = valid_upsert_validator_election.outputs[0].amount
|
||||
public_key0 = input0.owners_before[0]
|
||||
key0 = ed25519_node_keys[public_key0]
|
||||
with monkeypatch.context() as m:
|
||||
valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator)
|
||||
alice = generate_key_pair()
|
||||
|
||||
assert b.get_commited_votes(valid_upsert_validator_election) == 0
|
||||
input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys)
|
||||
|
||||
# delegate some votes to alice
|
||||
delegate_vote = Vote.generate(
|
||||
@ -128,8 +203,8 @@ def test_valid_election_votes_received(b_mock, valid_upsert_validator_election,
|
||||
[([alice.public_key], 4), ([key0.public_key], votes - 4)],
|
||||
election_ids=[valid_upsert_validator_election.id],
|
||||
).sign([key0.private_key])
|
||||
b_mock.models.store_bulk_transactions([delegate_vote])
|
||||
assert b_mock.get_commited_votes(valid_upsert_validator_election) == 0
|
||||
b.models.store_bulk_transactions([delegate_vote])
|
||||
assert b.get_commited_votes(valid_upsert_validator_election) == 0
|
||||
|
||||
election_public_key = election_id_to_public_key(valid_upsert_validator_election.id)
|
||||
alice_votes = delegate_vote.to_inputs()[0]
|
||||
@ -141,41 +216,61 @@ def test_valid_election_votes_received(b_mock, valid_upsert_validator_election,
|
||||
election_ids=[valid_upsert_validator_election.id],
|
||||
).sign([alice.private_key])
|
||||
|
||||
assert b_mock.validate_transaction(alice_casted_vote)
|
||||
b_mock.models.store_bulk_transactions([alice_casted_vote])
|
||||
assert b.validate_transaction(alice_casted_vote)
|
||||
b.models.store_bulk_transactions([alice_casted_vote])
|
||||
|
||||
# Check if the delegated vote is count as valid vote
|
||||
assert b_mock.get_commited_votes(valid_upsert_validator_election) == 2
|
||||
assert b.get_commited_votes(valid_upsert_validator_election) == 2
|
||||
|
||||
key0_casted_vote = Vote.generate(
|
||||
[key0_votes], [([election_public_key], votes - 4)], election_ids=[valid_upsert_validator_election.id]
|
||||
).sign([key0.private_key])
|
||||
|
||||
assert b_mock.validate_transaction(key0_casted_vote)
|
||||
b_mock.models.store_bulk_transactions([key0_casted_vote])
|
||||
assert b_mock.get_commited_votes(valid_upsert_validator_election) == votes - 2
|
||||
assert b.validate_transaction(key0_casted_vote)
|
||||
b.models.store_bulk_transactions([key0_casted_vote])
|
||||
assert b.get_commited_votes(valid_upsert_validator_election) == votes - 2
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
def test_valid_election_conclude(b_mock, valid_upsert_validator_election, ed25519_node_keys):
|
||||
def test_valid_election_conclude(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
voters = b.get_recipients_list()
|
||||
valid_upsert_validator_election = ValidatorElection.generate(
|
||||
[node_key.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key])
|
||||
|
||||
# Node 0: cast vote
|
||||
tx_vote0 = gen_vote(valid_upsert_validator_election, 0, ed25519_node_keys)
|
||||
|
||||
# check if the vote is valid even before the election doesn't exist
|
||||
with pytest.raises(ValidationError):
|
||||
assert b_mock.validate_transaction(tx_vote0)
|
||||
assert b.validate_transaction(tx_vote0)
|
||||
|
||||
# store election
|
||||
b_mock.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
# cannot conclude election as not votes exist
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election)
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election)
|
||||
|
||||
# validate vote
|
||||
assert b_mock.validate_transaction(tx_vote0)
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote0])
|
||||
assert b.validate_transaction(tx_vote0)
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election, [tx_vote0])
|
||||
|
||||
b_mock.models.store_bulk_transactions([tx_vote0])
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election)
|
||||
b.models.store_bulk_transactions([tx_vote0])
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election)
|
||||
|
||||
# Node 1: cast vote
|
||||
tx_vote1 = gen_vote(valid_upsert_validator_election, 1, ed25519_node_keys)
|
||||
@ -186,32 +281,32 @@ def test_valid_election_conclude(b_mock, valid_upsert_validator_election, ed2551
|
||||
# Node 3: cast vote
|
||||
tx_vote3 = gen_vote(valid_upsert_validator_election, 3, ed25519_node_keys)
|
||||
|
||||
assert b_mock.validate_transaction(tx_vote1)
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote1])
|
||||
assert b.validate_transaction(tx_vote1)
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election, [tx_vote1])
|
||||
|
||||
# 2/3 is achieved in the same block so the election can be.has_concludedd
|
||||
assert b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote1, tx_vote2])
|
||||
assert b.has_election_concluded(valid_upsert_validator_election, [tx_vote1, tx_vote2])
|
||||
|
||||
b_mock.models.store_bulk_transactions([tx_vote1])
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election)
|
||||
b.models.store_bulk_transactions([tx_vote1])
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election)
|
||||
|
||||
assert b_mock.validate_transaction(tx_vote2)
|
||||
assert b_mock.validate_transaction(tx_vote3)
|
||||
assert b.validate_transaction(tx_vote2)
|
||||
assert b.validate_transaction(tx_vote3)
|
||||
|
||||
# conclusion can be triggered my different votes in the same block
|
||||
assert b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote2])
|
||||
assert b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote2, tx_vote3])
|
||||
assert b.has_election_concluded(valid_upsert_validator_election, [tx_vote2])
|
||||
assert b.has_election_concluded(valid_upsert_validator_election, [tx_vote2, tx_vote3])
|
||||
|
||||
b_mock.models.store_bulk_transactions([tx_vote2])
|
||||
b.models.store_bulk_transactions([tx_vote2])
|
||||
|
||||
# Once the blockchain records >2/3 of the votes the election is assumed to be.has_concludedd
|
||||
# so any invocation of `.has_concluded` for that election should return False
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election)
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election)
|
||||
|
||||
# Vote is still valid but the election cannot be.has_concluded as it it assumed that it has
|
||||
# been.has_concludedd before
|
||||
assert b_mock.validate_transaction(tx_vote3)
|
||||
assert not b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote3])
|
||||
assert b.validate_transaction(tx_vote3)
|
||||
assert not b.has_election_concluded(valid_upsert_validator_election, [tx_vote3])
|
||||
|
||||
|
||||
@pytest.mark.abci
|
||||
|
@ -21,72 +21,176 @@ from transactions.common.exceptions import (
|
||||
pytestmark = pytest.mark.bdb
|
||||
|
||||
|
||||
def test_upsert_validator_valid_election(b_mock, new_validator, node_key):
|
||||
voters = b_mock.get_recipients_list()
|
||||
def test_upsert_validator_valid_election(monkeypatch, b, network_validators, new_validator, node_key):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
voters = b.get_recipients_list()
|
||||
election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||
[node_key.private_key]
|
||||
)
|
||||
assert b_mock.validate_election(election)
|
||||
assert b.validate_election(election)
|
||||
m.undo()
|
||||
|
||||
|
||||
def test_upsert_validator_invalid_election_public_key(b_mock, new_validator, node_key):
|
||||
def test_upsert_validator_invalid_election_public_key(monkeypatch, b, network_validators, new_validator, node_key):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
from transactions.common.exceptions import InvalidPublicKey
|
||||
|
||||
for iv in ["ed25519-base32", "ed25519-base64"]:
|
||||
new_validator[0]["data"]["public_key"]["type"] = iv
|
||||
voters = b_mock.get_recipients_list()
|
||||
voters = b.get_recipients_list()
|
||||
|
||||
with pytest.raises(InvalidPublicKey):
|
||||
ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
||||
ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||
[node_key.private_key]
|
||||
)
|
||||
m.undo()
|
||||
|
||||
|
||||
def test_upsert_validator_invalid_power_election(b_mock, new_validator, node_key):
|
||||
voters = b_mock.get_recipients_list()
|
||||
def test_upsert_validator_invalid_power_election(monkeypatch, b, network_validators, new_validator, node_key):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
voters = b.get_recipients_list()
|
||||
new_validator[0]["data"]["power"] = 30
|
||||
|
||||
election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||
[node_key.private_key]
|
||||
)
|
||||
with pytest.raises(InvalidPowerChange):
|
||||
b_mock.validate_election(election)
|
||||
b.validate_election(election)
|
||||
m.undo()
|
||||
|
||||
|
||||
def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_key):
|
||||
def test_upsert_validator_invalid_proposed_election(monkeypatch, b, network_validators, new_validator, node_key):
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
alice = generate_key_pair()
|
||||
voters = b_mock.get_recipients_list()
|
||||
election = ValidatorElection.generate([alice.public_key], voters, new_validator, None).sign([alice.private_key])
|
||||
with pytest.raises(InvalidProposer):
|
||||
b_mock.validate_election(election)
|
||||
|
||||
|
||||
def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_key):
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
alice = generate_key_pair()
|
||||
voters = b_mock.get_recipients_list()
|
||||
election = ValidatorElection.generate([node_key.public_key, alice.public_key], voters, new_validator, None).sign(
|
||||
[node_key.private_key, alice.private_key]
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
alice = generate_key_pair()
|
||||
voters = b.get_recipients_list()
|
||||
election = ValidatorElection.generate([alice.public_key], voters, new_validator, None).sign(
|
||||
[alice.private_key]
|
||||
)
|
||||
with pytest.raises(InvalidProposer):
|
||||
b.validate_election(election)
|
||||
|
||||
|
||||
def test_upsert_validator_invalid_inputs_election(monkeypatch, b, network_validators, new_validator, node_key):
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
alice = generate_key_pair()
|
||||
voters = b.get_recipients_list()
|
||||
election = ValidatorElection.generate(
|
||||
[node_key.public_key, alice.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key, alice.private_key])
|
||||
with pytest.raises(MultipleInputsError):
|
||||
b_mock.validate_election(election)
|
||||
b.validate_election(election)
|
||||
m.undo()
|
||||
|
||||
|
||||
@patch("transactions.types.elections.election.uuid4", lambda: "mock_uuid4")
|
||||
def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixed_seed_election):
|
||||
voters = b_mock.get_recipients_list()
|
||||
def test_upsert_validator_invalid_election(monkeypatch, b, network_validators, new_validator, node_key):
|
||||
def mock_get_validators(self, height):
|
||||
validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
voters = b.get_recipients_list()
|
||||
duplicate_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||
[node_key.private_key]
|
||||
)
|
||||
voters = b.get_recipients_list()
|
||||
fixed_seed_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||
[node_key.private_key]
|
||||
)
|
||||
|
||||
with pytest.raises(DuplicateTransaction):
|
||||
b_mock.validate_election(fixed_seed_election, [duplicate_election])
|
||||
b.validate_election(fixed_seed_election, [duplicate_election])
|
||||
|
||||
b_mock.models.store_bulk_transactions([fixed_seed_election])
|
||||
b.models.store_bulk_transactions([fixed_seed_election])
|
||||
|
||||
with pytest.raises(DuplicateTransaction):
|
||||
b_mock.validate_election(duplicate_election)
|
||||
b.validate_election(duplicate_election)
|
||||
|
||||
# Try creating an election with incomplete voter set
|
||||
invalid_election = ValidatorElection.generate([node_key.public_key], voters[1:], new_validator, None).sign(
|
||||
@ -94,9 +198,9 @@ def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixe
|
||||
)
|
||||
|
||||
with pytest.raises(UnequalValidatorSet):
|
||||
b_mock.validate_election(invalid_election)
|
||||
b.validate_election(invalid_election)
|
||||
|
||||
recipients = b_mock.get_recipients_list()
|
||||
recipients = b.get_recipients_list()
|
||||
altered_recipients = []
|
||||
for r in recipients:
|
||||
([r_public_key], voting_power) = r
|
||||
@ -108,23 +212,89 @@ def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixe
|
||||
)
|
||||
|
||||
with pytest.raises(UnequalValidatorSet):
|
||||
b_mock.validate_election(tx_election)
|
||||
b.validate_election(tx_election)
|
||||
m.undo()
|
||||
|
||||
|
||||
def test_get_status_ongoing(b, ongoing_validator_election, new_validator):
|
||||
def test_get_status_ongoing(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys):
|
||||
def mock_get_validators(self, height):
|
||||
_validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
_validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return _validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
from planetmint.backend import schema, query
|
||||
from planetmint.abci.block import Block
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
voters = b.get_recipients_list()
|
||||
valid_upsert_validator_election = ValidatorElection.generate(
|
||||
[node_key.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key])
|
||||
|
||||
validators = b.models.get_validators(height=1)
|
||||
genesis_validators = {"validators": validators, "height": 0}
|
||||
query.store_validator_set(b.models.connection, genesis_validators)
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False)
|
||||
block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id])
|
||||
b.models.store_block(block_1._asdict())
|
||||
|
||||
status = ValidatorElection.ONGOING
|
||||
resp = b.get_election_status(ongoing_validator_election)
|
||||
resp = b.get_election_status(valid_upsert_validator_election)
|
||||
assert resp == status
|
||||
m.undo()
|
||||
|
||||
|
||||
def test_get_status_concluded(b, concluded_election, new_validator):
|
||||
def test_get_status_concluded(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys):
|
||||
def mock_get_validators(self, height):
|
||||
_validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
_validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return _validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
from planetmint.backend import schema, query
|
||||
from planetmint.abci.block import Block
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
voters = b.get_recipients_list()
|
||||
valid_upsert_validator_election = ValidatorElection.generate(
|
||||
[node_key.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key])
|
||||
|
||||
validators = b.models.get_validators(height=1)
|
||||
genesis_validators = {"validators": validators, "height": 0}
|
||||
query.store_validator_set(b.models.connection, genesis_validators)
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False)
|
||||
block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id])
|
||||
b.models.store_block(block_1._asdict())
|
||||
query.store_election(b.models.connection, valid_upsert_validator_election.id, 2, is_concluded=True)
|
||||
|
||||
status = ValidatorElection.CONCLUDED
|
||||
resp = b.get_election_status(concluded_election)
|
||||
resp = b.get_election_status(valid_upsert_validator_election)
|
||||
assert resp == status
|
||||
m.undo()
|
||||
|
||||
|
||||
def test_get_status_inconclusive(b, inconclusive_election, new_validator):
|
||||
def set_block_height_to_3():
|
||||
def test_get_status_inconclusive(monkeypatch, b, network_validators, node_key, new_validator):
|
||||
def set_block_height_to_3(self):
|
||||
return {"height": 3}
|
||||
|
||||
def custom_mock_get_validators(height):
|
||||
@ -167,20 +337,89 @@ def test_get_status_inconclusive(b, inconclusive_election, new_validator):
|
||||
},
|
||||
]
|
||||
|
||||
b.models.get_validators = custom_mock_get_validators
|
||||
b.models.get_latest_block = set_block_height_to_3
|
||||
def mock_get_validators(self, height):
|
||||
_validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
_validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return _validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
from planetmint.backend import schema, query
|
||||
from planetmint.abci.block import Block
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
voters = b.get_recipients_list()
|
||||
valid_upsert_validator_election = ValidatorElection.generate(
|
||||
[node_key.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key])
|
||||
|
||||
validators = b.models.get_validators(height=1)
|
||||
genesis_validators = {"validators": validators, "height": 0}
|
||||
query.store_validator_set(b.models.connection, genesis_validators)
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False)
|
||||
block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id])
|
||||
b.models.store_block(block_1._asdict())
|
||||
|
||||
validators = b.models.get_validators(height=1)
|
||||
validators[0]["voting_power"] = 15
|
||||
validator_update = {"validators": validators, "height": 2, "election_id": "some_other_election"}
|
||||
|
||||
query.store_validator_set(b.models.connection, validator_update)
|
||||
m.undo()
|
||||
with monkeypatch.context() as m2:
|
||||
m2.setattr(DataAccessor, "get_validators", custom_mock_get_validators)
|
||||
m2.setattr(DataAccessor, "get_latest_block", set_block_height_to_3)
|
||||
status = ValidatorElection.INCONCLUSIVE
|
||||
resp = b.get_election_status(inconclusive_election)
|
||||
resp = b.get_election_status(valid_upsert_validator_election)
|
||||
assert resp == status
|
||||
m2.undo()
|
||||
|
||||
|
||||
def test_upsert_validator_show(caplog, ongoing_validator_election, b):
|
||||
def test_upsert_validator_show(monkeypatch, caplog, b, node_key, new_validator, network_validators):
|
||||
from planetmint.commands.planetmint import run_election_show
|
||||
|
||||
election_id = ongoing_validator_election.id
|
||||
public_key = public_key_to_base64(ongoing_validator_election.assets[0]["data"]["public_key"]["value"])
|
||||
power = ongoing_validator_election.assets[0]["data"]["power"]
|
||||
node_id = ongoing_validator_election.assets[0]["data"]["node_id"]
|
||||
def mock_get_validators(self, height):
|
||||
_validators = []
|
||||
for public_key, power in network_validators.items():
|
||||
_validators.append(
|
||||
{
|
||||
"public_key": {"type": "ed25519-base64", "value": public_key},
|
||||
"voting_power": power,
|
||||
}
|
||||
)
|
||||
return _validators
|
||||
|
||||
with monkeypatch.context() as m:
|
||||
from planetmint.model.dataaccessor import DataAccessor
|
||||
from planetmint.backend import schema, query
|
||||
from planetmint.abci.block import Block
|
||||
|
||||
m.setattr(DataAccessor, "get_validators", mock_get_validators)
|
||||
|
||||
voters = b.get_recipients_list()
|
||||
valid_upsert_validator_election = ValidatorElection.generate(
|
||||
[node_key.public_key], voters, new_validator, None
|
||||
).sign([node_key.private_key])
|
||||
|
||||
validators = b.models.get_validators(height=1)
|
||||
genesis_validators = {"validators": validators, "height": 0}
|
||||
query.store_validator_set(b.models.connection, genesis_validators)
|
||||
b.models.store_bulk_transactions([valid_upsert_validator_election])
|
||||
query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False)
|
||||
block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id])
|
||||
b.models.store_block(block_1._asdict())
|
||||
election_id = valid_upsert_validator_election.id
|
||||
public_key = public_key_to_base64(valid_upsert_validator_election.assets[0]["data"]["public_key"]["value"])
|
||||
power = valid_upsert_validator_election.assets[0]["data"]["power"]
|
||||
node_id = valid_upsert_validator_election.assets[0]["data"]["node_id"]
|
||||
status = ValidatorElection.ONGOING
|
||||
|
||||
show_args = Namespace(action="show", election_id=election_id)
|
||||
@ -188,3 +427,4 @@ def test_upsert_validator_show(caplog, ongoing_validator_election, b):
|
||||
msg = run_election_show(show_args, b)
|
||||
|
||||
assert msg == f"public_key={public_key}\npower={power}\nnode_id={node_id}\nstatus={status}"
|
||||
m.undo()
|
||||
|
@ -3,7 +3,6 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||
import multiprocessing
|
||||
from hashlib import sha3_256
|
||||
|
||||
import base58
|
||||
import base64
|
||||
@ -11,7 +10,6 @@ import random
|
||||
|
||||
from functools import singledispatch
|
||||
|
||||
from planetmint import backend
|
||||
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
|
||||
from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
|
||||
from planetmint.backend.schema import TABLES
|
||||
@ -20,7 +18,7 @@ from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
||||
from transactions.types.assets.create import Create
|
||||
from transactions.types.elections.vote import Vote
|
||||
from transactions.types.elections.validator_utils import election_id_to_public_key
|
||||
from planetmint.abci.utils import merkleroot, key_to_base64
|
||||
from planetmint.abci.utils import key_to_base64
|
||||
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
|
||||
|
||||
|
||||
@ -127,78 +125,6 @@ def generate_election(b, cls, public_key, private_key, asset_data, voter_keys):
|
||||
return election, votes
|
||||
|
||||
|
||||
def delete_unspent_outputs(connection, *unspent_outputs):
|
||||
"""Deletes the given ``unspent_outputs`` (utxos).
|
||||
|
||||
Args:
|
||||
*unspent_outputs (:obj:`tuple` of :obj:`dict`): Variable
|
||||
length tuple or list of unspent outputs.
|
||||
"""
|
||||
if unspent_outputs:
|
||||
return backend.query.delete_unspent_outputs(connection, *unspent_outputs)
|
||||
|
||||
|
||||
def get_utxoset_merkle_root(connection):
|
||||
"""Returns the merkle root of the utxoset. This implies that
|
||||
the utxoset is first put into a merkle tree.
|
||||
|
||||
For now, the merkle tree and its root will be computed each
|
||||
time. This obviously is not efficient and a better approach
|
||||
that limits the repetition of the same computation when
|
||||
unnecesary should be sought. For instance, future optimizations
|
||||
could simply re-compute the branches of the tree that were
|
||||
affected by a change.
|
||||
|
||||
The transaction hash (id) and output index should be sufficient
|
||||
to uniquely identify a utxo, and consequently only that
|
||||
information from a utxo record is needed to compute the merkle
|
||||
root. Hence, each node of the merkle tree should contain the
|
||||
tuple (txid, output_index).
|
||||
|
||||
.. important:: The leaves of the tree will need to be sorted in
|
||||
some kind of lexicographical order.
|
||||
|
||||
Returns:
|
||||
str: Merkle root in hexadecimal form.
|
||||
"""
|
||||
utxoset = backend.query.get_unspent_outputs(connection)
|
||||
# TODO Once ready, use the already pre-computed utxo_hash field.
|
||||
# See common/transactions.py for details.
|
||||
hashes = [
|
||||
sha3_256("{}{}".format(utxo["transaction_id"], utxo["output_index"]).encode()).digest() for utxo in utxoset
|
||||
]
|
||||
# TODO Notice the sorted call!
|
||||
return merkleroot(sorted(hashes))
|
||||
|
||||
|
||||
def store_unspent_outputs(connection, *unspent_outputs):
|
||||
"""Store the given ``unspent_outputs`` (utxos).
|
||||
|
||||
Args:
|
||||
*unspent_outputs (:obj:`tuple` of :obj:`dict`): Variable
|
||||
length tuple or list of unspent outputs.
|
||||
"""
|
||||
if unspent_outputs:
|
||||
return backend.query.store_unspent_outputs(connection, *unspent_outputs)
|
||||
|
||||
|
||||
def update_utxoset(connection, transaction):
|
||||
"""
|
||||
Update the UTXO set given ``transaction``. That is, remove
|
||||
the outputs that the given ``transaction`` spends, and add the
|
||||
outputs that the given ``transaction`` creates.
|
||||
|
||||
Args:
|
||||
transaction (:obj:`~planetmint.models.Transaction`): A new
|
||||
transaction incoming into the system for which the UTXOF
|
||||
set needs to be updated.
|
||||
"""
|
||||
spent_outputs = [spent_output for spent_output in transaction.spent_outputs]
|
||||
if spent_outputs:
|
||||
delete_unspent_outputs(connection, *spent_outputs)
|
||||
store_unspent_outputs(connection, *[utxo._asdict() for utxo in transaction.unspent_outputs])
|
||||
|
||||
|
||||
class ProcessGroup(object):
|
||||
def __init__(self, concurrency=None, group=None, target=None, name=None, args=None, kwargs=None, daemon=None):
|
||||
self.concurrency = concurrency or multiprocessing.cpu_count()
|
||||
|
@ -8,6 +8,16 @@ import pytest
|
||||
BLOCKS_ENDPOINT = "/api/v1/blocks/"
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.usefixtures("inputs")
|
||||
def test_get_latest_block(client):
|
||||
res = client.get(BLOCKS_ENDPOINT + "latest")
|
||||
assert res.status_code == 200
|
||||
assert len(res.json["transaction_ids"]) == 10
|
||||
assert res.json["app_hash"] == "hash3"
|
||||
assert res.json["height"] == 3
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.usefixtures("inputs")
|
||||
def test_get_block_returns_404_if_not_found(client):
|
||||
@ -55,16 +65,6 @@ def test_get_blocks_by_txid_endpoint_returns_400_bad_query_params(client):
|
||||
assert res.json == {"message": "Unknown arguments: status"}
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.usefixtures("inputs")
|
||||
def test_get_latest_block(client):
|
||||
res = client.get(BLOCKS_ENDPOINT + "latest")
|
||||
assert res.status_code == 200
|
||||
assert len(res.json["transaction_ids"]) == 10
|
||||
assert res.json["app_hash"] == "hash3"
|
||||
assert res.json["height"] == 3
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.usefixtures("inputs")
|
||||
def test_get_block_by_height(client):
|
||||
|
@ -16,8 +16,8 @@ OUTPUTS_ENDPOINT = "/api/v1/outputs/"
|
||||
@pytest.mark.userfixtures("inputs")
|
||||
def test_get_outputs_endpoint(client, user_pk):
|
||||
m = MagicMock()
|
||||
m.txid = "a"
|
||||
m.output = 0
|
||||
m.transaction_id = "a"
|
||||
m.index = 0
|
||||
with patch("planetmint.model.dataaccessor.DataAccessor.get_outputs_filtered") as gof:
|
||||
gof.return_value = [m, m]
|
||||
res = client.get(OUTPUTS_ENDPOINT + "?public_key={}".format(user_pk))
|
||||
@ -28,8 +28,8 @@ def test_get_outputs_endpoint(client, user_pk):
|
||||
|
||||
def test_get_outputs_endpoint_unspent(client, user_pk):
|
||||
m = MagicMock()
|
||||
m.txid = "a"
|
||||
m.output = 0
|
||||
m.transaction_id = "a"
|
||||
m.index = 0
|
||||
with patch("planetmint.model.dataaccessor.DataAccessor.get_outputs_filtered") as gof:
|
||||
gof.return_value = [m]
|
||||
params = "?spent=False&public_key={}".format(user_pk)
|
||||
@ -43,8 +43,8 @@ def test_get_outputs_endpoint_unspent(client, user_pk):
|
||||
@pytest.mark.userfixtures("inputs")
|
||||
def test_get_outputs_endpoint_spent(client, user_pk):
|
||||
m = MagicMock()
|
||||
m.txid = "a"
|
||||
m.output = 0
|
||||
m.transaction_id = "a"
|
||||
m.index = 0
|
||||
with patch("planetmint.model.dataaccessor.DataAccessor.get_outputs_filtered") as gof:
|
||||
gof.return_value = [m]
|
||||
params = "?spent=true&public_key={}".format(user_pk)
|
||||
|
@ -23,10 +23,6 @@ from transactions.common.transaction_mode_types import (
|
||||
BROADCAST_TX_ASYNC,
|
||||
BROADCAST_TX_SYNC,
|
||||
)
|
||||
from transactions.common.transaction import (
|
||||
Input,
|
||||
TransactionLink,
|
||||
)
|
||||
from transactions.common.utils import _fulfillment_from_details
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
|
@ -8,29 +8,23 @@ import json
|
||||
import queue
|
||||
import threading
|
||||
import pytest
|
||||
import random
|
||||
import time
|
||||
|
||||
# from unittest.mock import patch
|
||||
from transactions.types.assets.create import Create
|
||||
from transactions.types.assets.transfer import Transfer
|
||||
from transactions.common import crypto
|
||||
from planetmint.ipc import events
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
# from planetmint import processes
|
||||
from planetmint.ipc import events # , POISON_PILL
|
||||
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT, EVENTS_ENDPOINT_BLOCKS
|
||||
from ipld import multihash, marshal
|
||||
from planetmint.web.websocket_dispatcher import Dispatcher
|
||||
|
||||
|
||||
class MockWebSocket:
|
||||
def __init__(self):
|
||||
self.received = []
|
||||
|
||||
def send_str(self, s):
|
||||
self.received.append(s)
|
||||
|
||||
|
||||
def test_eventify_block_works_with_any_transaction():
|
||||
from planetmint.web.websocket_dispatcher import Dispatcher
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
alice = generate_key_pair()
|
||||
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)]).sign([alice.private_key])
|
||||
@ -50,9 +44,6 @@ def test_eventify_block_works_with_any_transaction():
|
||||
|
||||
|
||||
def test_simplified_block_works():
|
||||
from planetmint.web.websocket_dispatcher import Dispatcher
|
||||
from transactions.common.crypto import generate_key_pair
|
||||
|
||||
alice = generate_key_pair()
|
||||
|
||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)]).sign([alice.private_key])
|
||||
@ -112,12 +103,12 @@ async def test_websocket_transaction_event(aiohttp_client):
|
||||
tx = Create.generate([user_pub], [([user_pub], 1)])
|
||||
tx = tx.sign([user_priv])
|
||||
|
||||
app = init_app(None)
|
||||
client = await aiohttp_client(app)
|
||||
myapp = init_app(None)
|
||||
client = await aiohttp_client(myapp)
|
||||
ws = await client.ws_connect(EVENTS_ENDPOINT)
|
||||
block = {"height": 1, "transactions": [tx]}
|
||||
blk_source = Dispatcher.get_queue_on_demand(app, "blk_source")
|
||||
tx_source = Dispatcher.get_queue_on_demand(app, "tx_source")
|
||||
blk_source = Dispatcher.get_queue_on_demand(myapp, "blk_source")
|
||||
tx_source = Dispatcher.get_queue_on_demand(myapp, "tx_source")
|
||||
block_event = events.Event(events.EventTypes.BLOCK_VALID, block)
|
||||
|
||||
await tx_source.put(block_event)
|
||||
@ -136,15 +127,12 @@ async def test_websocket_transaction_event(aiohttp_client):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_websocket_string_event(aiohttp_client):
|
||||
from planetmint.ipc.events import POISON_PILL
|
||||
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT
|
||||
|
||||
app = init_app(None)
|
||||
client = await aiohttp_client(app)
|
||||
myapp = init_app(None)
|
||||
client = await aiohttp_client(myapp)
|
||||
ws = await client.ws_connect(EVENTS_ENDPOINT)
|
||||
|
||||
blk_source = Dispatcher.get_queue_on_demand(app, "blk_source")
|
||||
tx_source = Dispatcher.get_queue_on_demand(app, "tx_source")
|
||||
blk_source = Dispatcher.get_queue_on_demand(myapp, "blk_source")
|
||||
tx_source = Dispatcher.get_queue_on_demand(myapp, "tx_source")
|
||||
|
||||
await tx_source.put("hack")
|
||||
await tx_source.put("the")
|
||||
@ -164,7 +152,7 @@ async def test_websocket_string_event(aiohttp_client):
|
||||
|
||||
|
||||
@pytest.mark.skip("Processes are not stopping properly, and the whole test suite would hang")
|
||||
def test_integration_from_webapi_to_websocket(monkeypatch, client, loop):
|
||||
def test_integration_from_webapi_to_websocket(monkeypatchonkeypatch, client, loop):
|
||||
# XXX: I think that the `pytest-aiohttp` plugin is sparkling too much
|
||||
# magic in the `asyncio` module: running this test without monkey-patching
|
||||
# `asycio.get_event_loop` (and without the `loop` fixture) raises a:
|
||||
@ -174,21 +162,13 @@ def test_integration_from_webapi_to_websocket(monkeypatch, client, loop):
|
||||
# plugin explicitely.
|
||||
monkeypatch.setattr("asyncio.get_event_loop", lambda: loop)
|
||||
|
||||
import json
|
||||
import random
|
||||
import aiohttp
|
||||
|
||||
# TODO processes does not exist anymore, when reactivating this test it
|
||||
# will fail because of this
|
||||
from planetmint import processes
|
||||
|
||||
# Start Planetmint
|
||||
processes.start()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
import time
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
ws_url = client.get("http://localhost:9984/api/v1/").json["_links"]["streams_v1"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user