Merge branch 'integration' of github.com:Chia-Network/chia-blockchain into integration
This commit is contained in:
commit
cc3b51a1a1
1
.flake8
1
.flake8
|
@ -1,3 +1,4 @@
|
||||||
[flake8]
|
[flake8]
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
exclude = ./typings/**/*
|
exclude = ./typings/**/*
|
||||||
|
ignore = E203,W503
|
||||||
|
|
|
@ -20,7 +20,9 @@ jobs:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew update && brew install gmp boost || echo ""
|
brew update && brew install gmp boost openssl || echo ""
|
||||||
|
sudo apt-get install libboost-all-dev || echo ""
|
||||||
|
sudo apt-get install libssl-dev || echo ""
|
||||||
sh install.sh
|
sh install.sh
|
||||||
- name: Test proof of space
|
- name: Test proof of space
|
||||||
run: |
|
run: |
|
||||||
|
@ -38,7 +40,7 @@ jobs:
|
||||||
cd ../../../
|
cd ../../../
|
||||||
- name: Lint source with flake8
|
- name: Lint source with flake8
|
||||||
run: |
|
run: |
|
||||||
./.venv/bin/flake8 src --exclude src/wallet
|
./.venv/bin/flake8 src --exclude src/wallet/electron/node_modules
|
||||||
- name: Lint source with mypy
|
- name: Lint source with mypy
|
||||||
run: |
|
run: |
|
||||||
./.venv/bin/mypy src tests
|
./.venv/bin/mypy src tests
|
||||||
|
|
|
@ -62,4 +62,8 @@ pip-delete-this-directory.txt
|
||||||
# Packaging
|
# Packaging
|
||||||
chia-blockchain.tar.gz
|
chia-blockchain.tar.gz
|
||||||
|
|
||||||
|
# Electron wallet node modules
|
||||||
src/wallet/electron/node_modules/
|
src/wallet/electron/node_modules/
|
||||||
|
|
||||||
|
# Bip158 build dir
|
||||||
|
lib/bip158/build/
|
|
@ -5,7 +5,7 @@ To install the chia-blockchain node, follow the instructions according to your o
|
||||||
Make sure [brew](https://brew.sh/) is available before starting the setup.
|
Make sure [brew](https://brew.sh/) is available before starting the setup.
|
||||||
```bash
|
```bash
|
||||||
brew upgrade python
|
brew upgrade python
|
||||||
brew install cmake gmp
|
brew install cmake gmp boost openssl
|
||||||
|
|
||||||
git clone https://github.com/Chia-Network/chia-blockchain.git
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
cd chia-blockchain
|
cd chia-blockchain
|
||||||
|
|
200
README.md
200
README.md
|
@ -11,7 +11,203 @@ For alpha testnet most should only install harvesters, farmers, plotter and full
|
||||||
To install the chia-blockchain node, follow [these](INSTALL.md) instructions according to your operating system.
|
To install the chia-blockchain node, follow [these](INSTALL.md) instructions according to your operating system.
|
||||||
|
|
||||||
|
|
||||||
## Step 2: Generate keys
|
```bash
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install build-essential git cmake libgmp3-dev --no-install-recommends
|
||||||
|
sudo apt-get install python3-dev python3-venv --no-install-recommends
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install.sh
|
||||||
|
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
### Amazon Linux 2
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo yum update
|
||||||
|
sudo yum install gcc-c++ cmake3 wget git openssl openssl-devel
|
||||||
|
sudo yum install python3 python3-devel libffi-devel gmp-devel
|
||||||
|
|
||||||
|
# CMake - add a symlink for cmake3 - required by blspy
|
||||||
|
sudo ln -s /usr/bin/cmake3 /usr/local/bin/cmake
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install.sh
|
||||||
|
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
### CentOS 7.7
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo yum update
|
||||||
|
sudo yum install centos-release-scl-rh epel-release
|
||||||
|
sudo yum install devtoolset-8-toolchain cmake3 libffi-devel
|
||||||
|
sudo yum install gmp-devel libsqlite3x-devel
|
||||||
|
sudo yum install wget git openssl openssl-devel
|
||||||
|
|
||||||
|
# CMake - add a symlink for cmake3 - required by blspy
|
||||||
|
sudo ln -s /usr/bin/cmake3 /usr/local/bin/cmake
|
||||||
|
|
||||||
|
scl enable devtoolset-8 bash
|
||||||
|
|
||||||
|
# Install Python 3.7.5 (current rpm's are 3.6.x)
|
||||||
|
wget https://www.python.org/ftp/python/3.7.5/Python-3.7.5.tgz
|
||||||
|
tar -zxvf Python-3.7.5.tgz; cd Python-3.7.5
|
||||||
|
./configure --enable-optimizations; sudo make install; cd ..
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install.sh
|
||||||
|
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
### RHEL 8.1
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo yum update
|
||||||
|
sudo yum install gcc-c++ cmake3 git openssl openssl-devel
|
||||||
|
sudo yum install wget make libffi-devel gmp-devel sqlite-devel
|
||||||
|
|
||||||
|
# Install Python 3.7.5 (current rpm's are 3.6.x)
|
||||||
|
wget https://www.python.org/ftp/python/3.7.5/Python-3.7.5.tgz
|
||||||
|
tar -zxvf Python-3.7.5.tgz; cd Python-3.7.5
|
||||||
|
./configure --enable-optimizations; sudo make install; cd ..
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install.sh
|
||||||
|
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
### Windows (WSL + Ubuntu)
|
||||||
|
#### Install WSL + Ubuntu 18.04 LTS, upgrade to Ubuntu 19.x
|
||||||
|
|
||||||
|
This will require multiple reboots. From an Administrator PowerShell
|
||||||
|
`Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux`
|
||||||
|
and then
|
||||||
|
`Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform`.
|
||||||
|
Once that is complete, install Ubuntu 18.04 LTS from the Windows Store.
|
||||||
|
```bash
|
||||||
|
# Upgrade to 19.x
|
||||||
|
sudo nano /etc/update-manager/release-upgrades
|
||||||
|
# Change "Prompt=lts" to "Prompt=normal" save and exit
|
||||||
|
|
||||||
|
sudo apt-get -y update
|
||||||
|
sudo apt-get -y upgrade
|
||||||
|
sudo do-release-upgrade
|
||||||
|
|
||||||
|
sudo apt-get install -y build-essential cmake python3-dev python3-venv software-properties-common libgmp3-dev --no-install-recommends
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sudo sh install.sh
|
||||||
|
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Alternate method for Ubuntu 18.04 LTS
|
||||||
|
In `./install.sh`:
|
||||||
|
Change `python3` to `python3.7`
|
||||||
|
Each line that starts with `pip ...` becomes `python3.7 -m pip ...`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get -y update
|
||||||
|
sudo apt-get install -y build-essential cmake python3-dev python3-venv software-properties-common libgmp3-dev --no-install-recommends
|
||||||
|
|
||||||
|
# Install python3.7 with ppa
|
||||||
|
sudo add-apt-repository -y ppa:deadsnakes/ppa
|
||||||
|
sudo apt-get -y update
|
||||||
|
sudo apt-get install -y python3.7 python3.7-venv python3.7-dev
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sudo sh install.sh
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
Make sure [brew](https://brew.sh/) is available before starting the setup.
|
||||||
|
```bash
|
||||||
|
brew upgrade python
|
||||||
|
brew install cmake gmp
|
||||||
|
|
||||||
|
git clone https://github.com/Chia-Network/chia-blockchain.git
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install.sh
|
||||||
|
. .venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Step 2: Install timelord (optional)
|
||||||
|
Note: this step is needed only if you intend to run a timelord or a local simulation.
|
||||||
|
These assume you've already successfully installed harvester, farmer, plotting, and full node above. boost 1.66 or newer is required on all platforms.
|
||||||
|
### Ubuntu/Debian
|
||||||
|
```bash
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install_timelord.sh
|
||||||
|
```
|
||||||
|
### Amazon Linux 2 and CentOS 7.7
|
||||||
|
```bash
|
||||||
|
#Only for Amazon Linux 2
|
||||||
|
sudo amazon-linux-extras install epel
|
||||||
|
|
||||||
|
sudo yum install mpfr-devel
|
||||||
|
|
||||||
|
# Install Boost 1.72.0
|
||||||
|
wget https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.gz
|
||||||
|
tar -zxvf boost_1_72_0.tar.gz
|
||||||
|
cd boost_1_72_0
|
||||||
|
./bootstrap.sh --prefix=/usr/local
|
||||||
|
sudo ./b2 install --prefix=/usr/local --with=all; cd ..
|
||||||
|
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
|
||||||
|
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install_timelord.sh
|
||||||
|
```
|
||||||
|
### RHEL 8.1
|
||||||
|
```bash
|
||||||
|
sudo yum install mpfr-devel boost boost-devel
|
||||||
|
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install_timelord.sh
|
||||||
|
```
|
||||||
|
### Windows (WSL + Ubuntu)
|
||||||
|
#### Install WSL + Ubuntu upgraded to 19.x
|
||||||
|
```bash
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install_timelord.sh
|
||||||
|
```
|
||||||
|
#### Alternate method for Ubuntu 18.04
|
||||||
|
```bash
|
||||||
|
# Install boost 1.70 with ppa
|
||||||
|
sudo add-apt-repository -y ppa:mhier/libboost-latest
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libboost1.70 libboost1.70-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
```bash
|
||||||
|
brew install boost
|
||||||
|
|
||||||
|
cd chia-blockchain
|
||||||
|
|
||||||
|
sh install_timelord.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: Generate keys
|
||||||
First, create some keys by running the following script:
|
First, create some keys by running the following script:
|
||||||
```bash
|
```bash
|
||||||
python -m scripts.regenerate_keys
|
python -m scripts.regenerate_keys
|
||||||
|
@ -85,5 +281,3 @@ You can also use the [HTTP RPC](https://github.com/Chia-Network/chia-blockchain/
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:8555/get_blockchain_state
|
curl -X POST http://localhost:8555/get_blockchain_state
|
||||||
```
|
```
|
||||||
|
|
||||||
After installing, follow the remaining instructions in [README.md](README.md) to run the software.
|
|
||||||
|
|
|
@ -8,10 +8,13 @@ ENDIF()
|
||||||
|
|
||||||
project(chiabip158)
|
project(chiabip158)
|
||||||
|
|
||||||
|
link_directories(/usr/local/opt/openssl/lib)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${INCLUDE_DIRECTORIES}
|
${INCLUDE_DIRECTORIES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
/usr/local/opt/openssl/include
|
/usr/local/opt/openssl/include
|
||||||
|
/usr/local/opt/boost/include
|
||||||
)
|
)
|
||||||
|
|
||||||
set (CMAKE_CXX_FLAGS "-DHAVE_WORKING_BOOST_SLEEP -g -O3 -Wall -msse2 -msse -march=native -std=c++14 -maes")
|
set (CMAKE_CXX_FLAGS "-DHAVE_WORKING_BOOST_SLEEP -g -O3 -Wall -msse2 -msse -march=native -std=c++14 -maes")
|
||||||
|
@ -29,6 +32,7 @@ add_executable(bip158
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(bip158 biplib -lboost_system -lpthread -lboost_thread -lboost_filesystem -lssl -lcrypto)
|
find_package(Boost COMPONENTS system filesystem thread REQUIRED)
|
||||||
target_link_libraries(chiabip158 PRIVATE biplib -lboost_system -lpthread -lboost_thread -lboost_filesystem -lssl -lcrypto)
|
|
||||||
|
|
||||||
|
target_link_libraries(bip158 biplib ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} -lpthread -lssl -lcrypto)
|
||||||
|
target_link_libraries(chiabip158 PRIVATE biplib ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} -lpthread -lssl -lcrypto)
|
||||||
|
|
|
@ -29,6 +29,16 @@ PyBIP158::PyBIP158(std::vector< std::vector< unsigned char > >& hashes)
|
||||||
filter=new GCSFilter({0, 0, 20, 1 << 20},elements);
|
filter=new GCSFilter({0, 0, 20, 1 << 20},elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyBIP158::PyBIP158(std::vector< unsigned char > & encoded_filter)
|
||||||
|
{
|
||||||
|
filter=new GCSFilter({0, 0, 20, 1 << 20}, encoded_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<unsigned char>& PyBIP158::GetEncoded()
|
||||||
|
{
|
||||||
|
return filter->GetEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
PyBIP158::~PyBIP158()
|
PyBIP158::~PyBIP158()
|
||||||
{
|
{
|
||||||
delete filter;
|
delete filter;
|
||||||
|
|
|
@ -25,6 +25,8 @@ public:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PyBIP158(std::vector< std::vector< unsigned char > >& hashes);
|
PyBIP158(std::vector< std::vector< unsigned char > >& hashes);
|
||||||
|
PyBIP158(std::vector< unsigned char > & encoded_filter);
|
||||||
|
const std::vector<unsigned char>& GetEncoded();
|
||||||
~PyBIP158();
|
~PyBIP158();
|
||||||
|
|
||||||
bool Match(std::vector< unsigned char >& hash);
|
bool Match(std::vector< unsigned char >& hash);
|
||||||
|
|
|
@ -24,6 +24,8 @@ PYBIND11_MODULE(chiabip158, mod) {
|
||||||
py::class_<PyBIP158, std::shared_ptr<PyBIP158>> clsPyBIP158(mod, "PyBIP158");
|
py::class_<PyBIP158, std::shared_ptr<PyBIP158>> clsPyBIP158(mod, "PyBIP158");
|
||||||
|
|
||||||
clsPyBIP158.def(py::init<std::vector< std::vector< unsigned char > >&>());
|
clsPyBIP158.def(py::init<std::vector< std::vector< unsigned char > >&>());
|
||||||
|
clsPyBIP158.def(py::init< std::vector< unsigned char > &>());
|
||||||
|
clsPyBIP158.def("GetEncoded",(const std::vector< unsigned char >& (PyBIP158::*)()) &PyBIP158::GetEncoded);
|
||||||
clsPyBIP158.def("Match", (bool (PyBIP158::*)(std::vector< unsigned char >&)) &PyBIP158::Match);
|
clsPyBIP158.def("Match", (bool (PyBIP158::*)(std::vector< unsigned char >&)) &PyBIP158::Match);
|
||||||
clsPyBIP158.def("MatchAny", (bool (PyBIP158::*)(std::vector< std::vector< unsigned char > >&)) &PyBIP158::MatchAny);
|
clsPyBIP158.def("MatchAny", (bool (PyBIP158::*)(std::vector< std::vector< unsigned char > >&)) &PyBIP158::MatchAny);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,20 @@ add_subdirectory(lib/pybind11)
|
||||||
pybind11_add_module(chiapos ${CMAKE_CURRENT_SOURCE_DIR}/python-bindings/chiapos.cpp)
|
pybind11_add_module(chiapos ${CMAKE_CURRENT_SOURCE_DIR}/python-bindings/chiapos.cpp)
|
||||||
|
|
||||||
set (CMAKE_CXX_FLAGS "-g -O3 -Wall -msse2 -msse -march=native -std=c++1z -maes")
|
set (CMAKE_CXX_FLAGS "-g -O3 -Wall -msse2 -msse -march=native -std=c++1z -maes")
|
||||||
|
try_run(CMAKE_AESNI_TEST_RUN_RESULT
|
||||||
|
CMAKE_AESNI_TEST_COMPILE_RESULT
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/cmake_aesni_test
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/cmake_aesni_test.cpp)
|
||||||
|
|
||||||
|
# Did compilation succeed and process return 0 (success)?
|
||||||
|
IF("${CMAKE_AESNI_TEST_COMPILE_RESULT}" AND ("${CMAKE_AESNI_TEST_RUN_RESULT}" EQUAL 0))
|
||||||
|
message(STATUS "AESNI Enabled")
|
||||||
|
set (CMAKE_CXX_FLAGS "-g -O3 -Wall -msse2 -msse -march=native -std=c++17 -maes")
|
||||||
|
ELSE()
|
||||||
|
message(STATUS "AESNI Disabled")
|
||||||
|
add_compile_definitions (DISABLE_AESNI)
|
||||||
|
set (CMAKE_CXX_FLAGS "-g -O3 -Wall -march=native -std=c++17")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
add_executable(ProofOfSpace
|
add_executable(ProofOfSpace
|
||||||
src/cli.cpp
|
src/cli.cpp
|
||||||
|
|
|
@ -1,270 +1,493 @@
|
||||||
// Copyright 2018 Chia Network Inc
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
// Some public domain code is taken from pycrypto:
|
|
||||||
// https://github.com/dlitz/pycrypto/blob/master/src/AESNI.c
|
|
||||||
//
|
|
||||||
// AESNI.c: AES using AES-NI instructions
|
|
||||||
//
|
|
||||||
// Written in 2013 by Sebastian Ramacher <sebastian@ramacher.at>
|
|
||||||
|
|
||||||
#ifndef SRC_CPP_AES_HPP_
|
|
||||||
#define SRC_CPP_AES_HPP_
|
|
||||||
|
|
||||||
#include <string.h> // for memcmp
|
|
||||||
#include <wmmintrin.h> // for intrinsics for AES-NI
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message of 128 bits with a 128 bit key, using
|
|
||||||
* 10 rounds of AES128 (9 full rounds and one final round). Uses AES-NI
|
|
||||||
* assembly instructions.
|
|
||||||
*/
|
|
||||||
#define DO_ENC_BLOCK_128(m, k) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
m = _mm_xor_si128(m, k[0]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[1]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[2]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[3]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[4]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[5]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[6]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[7]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[8]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[9]); \
|
|
||||||
m = _mm_aesenclast_si128(m, k[10]); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message of 128 bits with a 256 bit key, using
|
|
||||||
* 13 rounds of AES256 (13 full rounds and one final round). Uses
|
|
||||||
* AES-NI assembly instructions.
|
|
||||||
*/
|
|
||||||
#define DO_ENC_BLOCK_256(m, k) \
|
|
||||||
do {\
|
|
||||||
m = _mm_xor_si128(m, k[ 0]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 1]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 2]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 3]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 4]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 5]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 6]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 7]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 8]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 9]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[ 10]);\
|
|
||||||
m = _mm_aesenc_si128(m, k[ 11]);\
|
|
||||||
m = _mm_aesenc_si128(m, k[ 12]);\
|
|
||||||
m = _mm_aesenc_si128(m, k[ 13]);\
|
|
||||||
m = _mm_aesenclast_si128(m, k[ 14]);\
|
|
||||||
}while(0)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message of 128 bits with a 128 bit key, using
|
|
||||||
* 2 full rounds of AES128. Uses AES-NI assembly instructions.
|
|
||||||
*/
|
|
||||||
#define DO_ENC_BLOCK_2ROUND(m, k) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
m = _mm_xor_si128(m, k[0]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[1]); \
|
|
||||||
m = _mm_aesenc_si128(m, k[2]); \
|
|
||||||
} while (0)
|
|
||||||
/**
|
|
||||||
* Decrypts a ciphertext of 128 bits with a 128 bit key, using
|
|
||||||
* 10 rounds of AES128 (9 full rounds and one final round).
|
|
||||||
* Uses AES-NI assembly instructions.
|
|
||||||
*/
|
|
||||||
#define DO_DEC_BLOCK(m, k) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
m = _mm_xor_si128(m, k[10 + 0]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 1]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 2]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 3]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 4]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 5]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 6]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 7]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 8]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[10 + 9]); \
|
|
||||||
m = _mm_aesdeclast_si128(m, k[0]); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a ciphertext of 128 bits with a 128 bit key, using
|
|
||||||
* 2 full rounds of AES128. Uses AES-NI assembly instructions.
|
|
||||||
* Will not work unless key schedule is modified.
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
#define DO_DEC_BLOCK_2ROUND(m, k) \
|
|
||||||
do \
|
The code in this file is originally from the Tiny AES project, which is in the
|
||||||
{ \
|
public domain.
|
||||||
m = _mm_xor_si128(m, k[2 + 0]); \
|
|
||||||
m = _mm_aesdec_si128(m, k[2 + 1]); \
|
https://github.com/kokke/tiny-AES-c
|
||||||
m = _mm_aesdec_si128(m, k[2 + 2]); \
|
|
||||||
} while (0)
|
It has been heavily modified by Chia.
|
||||||
|
|
||||||
|
***
|
||||||
|
|
||||||
|
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
|
||||||
|
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
|
||||||
|
|
||||||
|
The implementation is verified against the test vectors in:
|
||||||
|
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
|
||||||
|
|
||||||
|
ECB-AES128
|
||||||
|
----------
|
||||||
|
|
||||||
|
plain-text:
|
||||||
|
6bc1bee22e409f96e93d7e117393172a
|
||||||
|
ae2d8a571e03ac9c9eb76fac45af8e51
|
||||||
|
30c81c46a35ce411e5fbc1191a0a52ef
|
||||||
|
f69f2445df4f9b17ad2b417be66c3710
|
||||||
|
|
||||||
|
key:
|
||||||
|
2b7e151628aed2a6abf7158809cf4f3c
|
||||||
|
|
||||||
|
resulting cipher
|
||||||
|
3ad77bb40d7a3660a89ecaf32466ef97
|
||||||
|
f5d3d58503b9699de785895a96fdbaaf
|
||||||
|
43b1cd7f598ece23881b00e3ed030688
|
||||||
|
7b0c785e27e8ad3f8223207104725dd4
|
||||||
|
|
||||||
|
|
||||||
|
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
|
||||||
|
You should pad the end of the string with zeros if this is not the case.
|
||||||
|
For AES192/256 the key size is proportionally larger.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static __m128i key_schedule[20]; // The expanded key
|
|
||||||
|
|
||||||
static __m128i aes128_keyexpand(__m128i key) {
|
/*****************************************************************************/
|
||||||
key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
|
/* Includes: */
|
||||||
key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
|
/*****************************************************************************/
|
||||||
return _mm_xor_si128(key, _mm_slli_si128(key, 4));
|
#include <stdint.h>
|
||||||
|
#include <string.h> // CBC mode, for memset
|
||||||
|
|
||||||
|
//#define DISABLE_AESNI
|
||||||
|
|
||||||
|
#ifndef DISABLE_AESNI
|
||||||
|
#include <cpuid.h>
|
||||||
|
#include "aesni.hpp"
|
||||||
|
|
||||||
|
bool bHasAES=false;
|
||||||
|
bool bCheckedAES=false;
|
||||||
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Defines: */
|
||||||
|
/*****************************************************************************/
|
||||||
|
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
|
||||||
|
#define Nb 4
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Private variables: */
|
||||||
|
/*****************************************************************************/
|
||||||
|
// state - array holding the intermediate results during decryption.
|
||||||
|
typedef uint8_t state_t[4][4];
|
||||||
|
|
||||||
|
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
|
||||||
|
// The numbers below can be computed dynamically trading ROM for RAM -
|
||||||
|
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
|
||||||
|
static const uint8_t sbox[256] = {
|
||||||
|
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||||
|
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||||
|
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||||
|
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||||
|
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||||
|
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||||
|
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||||
|
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||||
|
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||||
|
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||||
|
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||||
|
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||||
|
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||||
|
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||||
|
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||||
|
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||||
|
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
|
||||||
|
|
||||||
|
static const uint8_t rsbox[256] = {
|
||||||
|
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||||
|
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||||
|
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||||
|
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||||
|
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||||
|
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||||
|
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||||
|
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||||
|
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||||
|
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||||
|
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||||
|
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||||
|
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||||
|
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||||
|
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||||
|
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
|
||||||
|
|
||||||
|
// The round constant word array, Rcon[i], contains the values given by
|
||||||
|
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
|
||||||
|
static const uint8_t Rcon[11] = {
|
||||||
|
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
|
||||||
|
* that you can remove most of the elements in the Rcon array, because they are unused.
|
||||||
|
*
|
||||||
|
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
|
||||||
|
*
|
||||||
|
* "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
|
||||||
|
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Private functions: */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define getSBoxValue(num) (sbox[(num)])
|
||||||
|
|
||||||
|
|
||||||
|
// This function adds the round key to state.
|
||||||
|
// The round key is added to the state by an XOR function.
|
||||||
|
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
|
||||||
|
{
|
||||||
|
uint8_t i,j;
|
||||||
|
for (i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 4; ++j)
|
||||||
|
{
|
||||||
|
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define KEYEXP128_H(K1, K2, I, S) _mm_xor_si128(aes128_keyexpand(K1), \
|
// The SubBytes Function Substitutes the values in the
|
||||||
_mm_shuffle_epi32(_mm_aeskeygenassist_si128(K2, I), S))
|
// state matrix with values in an S-box.
|
||||||
|
static void SubBytes(state_t* state)
|
||||||
|
{
|
||||||
|
uint8_t i, j;
|
||||||
|
for (i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 4; ++j)
|
||||||
|
{
|
||||||
|
(*state)[j][i] = getSBoxValue((*state)[j][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define KEYEXP128(K, I) KEYEXP128_H(K, K, I, 0xff)
|
// The ShiftRows() function shifts the rows in the state to the left.
|
||||||
#define KEYEXP256(K1, K2, I) KEYEXP128_H(K1, K2, I, 0xff)
|
// Each row is shifted with different offset.
|
||||||
#define KEYEXP256_2(K1, K2) KEYEXP128_H(K1, K2, 0x00, 0xaa)
|
// Offset = Row number. So the first row is not shifted.
|
||||||
|
static void ShiftRows(state_t* state)
|
||||||
|
{
|
||||||
|
uint8_t temp;
|
||||||
|
|
||||||
// public API
|
// Rotate first row 1 columns to left
|
||||||
|
temp = (*state)[0][1];
|
||||||
|
(*state)[0][1] = (*state)[1][1];
|
||||||
|
(*state)[1][1] = (*state)[2][1];
|
||||||
|
(*state)[2][1] = (*state)[3][1];
|
||||||
|
(*state)[3][1] = temp;
|
||||||
|
|
||||||
|
// Rotate second row 2 columns to left
|
||||||
|
temp = (*state)[0][2];
|
||||||
|
(*state)[0][2] = (*state)[2][2];
|
||||||
|
(*state)[2][2] = temp;
|
||||||
|
|
||||||
|
temp = (*state)[1][2];
|
||||||
|
(*state)[1][2] = (*state)[3][2];
|
||||||
|
(*state)[3][2] = temp;
|
||||||
|
|
||||||
|
// Rotate third row 3 columns to left
|
||||||
|
temp = (*state)[0][3];
|
||||||
|
(*state)[0][3] = (*state)[3][3];
|
||||||
|
(*state)[3][3] = (*state)[2][3];
|
||||||
|
(*state)[2][3] = (*state)[1][3];
|
||||||
|
(*state)[1][3] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t xtime(uint8_t x)
|
||||||
|
{
|
||||||
|
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// MixColumns function mixes the columns of the state matrix
|
||||||
|
static void MixColumns(state_t* state)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
uint8_t Tmp, Tm, t;
|
||||||
|
for (i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
t = (*state)[i][0];
|
||||||
|
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
|
||||||
|
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
|
||||||
|
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
|
||||||
|
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
|
||||||
|
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RoundKey128[176]; // AES_KEYLEN 16 Key length in bytes
|
||||||
|
uint8_t RoundKey256[240]; // AES_KEYLEN 32 Key length in bytes
|
||||||
|
|
||||||
|
#define KEYNR256 14
|
||||||
|
#define KEYNR128 10
|
||||||
|
|
||||||
|
#define KEYNK256 8
|
||||||
|
#define KEYNK128 4
|
||||||
|
|
||||||
|
#define ENCRYPTNR256 14
|
||||||
|
#define ENCRYPTNR128 3
|
||||||
|
|
||||||
|
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
|
||||||
|
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key, int keyNr, int keyNk)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
uint8_t tempa[4]; // Used for the column/row operations
|
||||||
|
|
||||||
|
// The first round key is the key itself.
|
||||||
|
for (i = 0; i < keyNk; ++i)
|
||||||
|
{
|
||||||
|
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
|
||||||
|
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
|
||||||
|
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
|
||||||
|
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other round keys are found from the previous round keys.
|
||||||
|
for (i = keyNk; i < Nb * (keyNr + 1); ++i)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
k = (i - 1) * 4;
|
||||||
|
tempa[0]=RoundKey[k + 0];
|
||||||
|
tempa[1]=RoundKey[k + 1];
|
||||||
|
tempa[2]=RoundKey[k + 2];
|
||||||
|
tempa[3]=RoundKey[k + 3];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i % keyNk == 0)
|
||||||
|
{
|
||||||
|
// This function shifts the 4 bytes in a word to the left once.
|
||||||
|
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
|
||||||
|
|
||||||
|
// Function RotWord()
|
||||||
|
{
|
||||||
|
const uint8_t u8tmp = tempa[0];
|
||||||
|
tempa[0] = tempa[1];
|
||||||
|
tempa[1] = tempa[2];
|
||||||
|
tempa[2] = tempa[3];
|
||||||
|
tempa[3] = u8tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubWord() is a function that takes a four-byte input word and
|
||||||
|
// applies the S-box to each of the four bytes to produce an output word.
|
||||||
|
|
||||||
|
// Function Subword()
|
||||||
|
{
|
||||||
|
tempa[0] = getSBoxValue(tempa[0]);
|
||||||
|
tempa[1] = getSBoxValue(tempa[1]);
|
||||||
|
tempa[2] = getSBoxValue(tempa[2]);
|
||||||
|
tempa[3] = getSBoxValue(tempa[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
tempa[0] = tempa[0] ^ Rcon[i/keyNk];
|
||||||
|
}
|
||||||
|
|
||||||
|
// AES256 only
|
||||||
|
if ((keyNk==8)&&(i % keyNk == 4))
|
||||||
|
{
|
||||||
|
// Function Subword()
|
||||||
|
{
|
||||||
|
tempa[0] = getSBoxValue(tempa[0]);
|
||||||
|
tempa[1] = getSBoxValue(tempa[1]);
|
||||||
|
tempa[2] = getSBoxValue(tempa[2]);
|
||||||
|
tempa[3] = getSBoxValue(tempa[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
j = i * 4; k=(i - keyNk) * 4;
|
||||||
|
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
|
||||||
|
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
|
||||||
|
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
|
||||||
|
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loads an AES key. Can either be a 16 byte or 32 byte bytearray.
|
* Loads an AES key. Can either be a 16 byte or 32 byte bytearray.
|
||||||
*/
|
*/
|
||||||
void aes_load_key(uint8_t *enc_key, int keylen) {
|
void aes_load_key(uint8_t *enc_key, int keylen) {
|
||||||
switch (keylen) {
|
|
||||||
case 16: {
|
#ifndef DISABLE_AESNI
|
||||||
/* 128 bit key setup */
|
if(!bCheckedAES)
|
||||||
key_schedule[0] = _mm_loadu_si128((const __m128i*) enc_key);
|
{
|
||||||
key_schedule[1] = KEYEXP128(key_schedule[0], 0x01);
|
uint32_t eax, ebx, ecx, edx;
|
||||||
key_schedule[2] = KEYEXP128(key_schedule[1], 0x02);
|
|
||||||
key_schedule[3] = KEYEXP128(key_schedule[2], 0x04);
|
eax = ebx = ecx = edx = 0;
|
||||||
key_schedule[4] = KEYEXP128(key_schedule[3], 0x08);
|
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||||
key_schedule[5] = KEYEXP128(key_schedule[4], 0x10);
|
bHasAES=(ecx & bit_AES) > 0;
|
||||||
key_schedule[6] = KEYEXP128(key_schedule[5], 0x20);
|
bCheckedAES=true;
|
||||||
key_schedule[7] = KEYEXP128(key_schedule[6], 0x40);
|
}
|
||||||
key_schedule[8] = KEYEXP128(key_schedule[7], 0x80);
|
|
||||||
key_schedule[9] = KEYEXP128(key_schedule[8], 0x1B);
|
if(bHasAES)
|
||||||
key_schedule[10] = KEYEXP128(key_schedule[9], 0x36);
|
return ni_aes_load_key(enc_key, keylen);
|
||||||
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
|
switch(keylen){
|
||||||
|
case 32:
|
||||||
|
KeyExpansion(RoundKey256, enc_key, KEYNR256, KEYNK256);
|
||||||
break;
|
break;
|
||||||
}
|
case 16:
|
||||||
case 32: {
|
KeyExpansion(RoundKey128, enc_key, KEYNR128, KEYNK128);
|
||||||
/* 256 bit key setup */
|
|
||||||
key_schedule[0] = _mm_loadu_si128((const __m128i*) enc_key);
|
|
||||||
key_schedule[1] = _mm_loadu_si128((const __m128i*) (enc_key+16));
|
|
||||||
key_schedule[2] = KEYEXP256(key_schedule[0], key_schedule[1], 0x01);
|
|
||||||
key_schedule[3] = KEYEXP256_2(key_schedule[1], key_schedule[2]);
|
|
||||||
key_schedule[4] = KEYEXP256(key_schedule[2], key_schedule[3], 0x02);
|
|
||||||
key_schedule[5] = KEYEXP256_2(key_schedule[3], key_schedule[4]);
|
|
||||||
key_schedule[6] = KEYEXP256(key_schedule[4], key_schedule[5], 0x04);
|
|
||||||
key_schedule[7] = KEYEXP256_2(key_schedule[5], key_schedule[6]);
|
|
||||||
key_schedule[8] = KEYEXP256(key_schedule[6], key_schedule[7], 0x08);
|
|
||||||
key_schedule[9] = KEYEXP256_2(key_schedule[7], key_schedule[8]);
|
|
||||||
key_schedule[10] = KEYEXP256(key_schedule[8], key_schedule[9], 0x10);
|
|
||||||
key_schedule[11] = KEYEXP256_2(key_schedule[9], key_schedule[10]);
|
|
||||||
key_schedule[12] = KEYEXP256(key_schedule[10], key_schedule[11], 0x20);
|
|
||||||
key_schedule[13] = KEYEXP256_2(key_schedule[11], key_schedule[12]);
|
|
||||||
key_schedule[14] = KEYEXP256(key_schedule[12], key_schedule[13], 0x40);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Declares a global variable for efficiency.
|
/*
|
||||||
__m128i m_global;
|
* XOR 128 bits
|
||||||
|
*/
|
||||||
|
static inline void xor128(const uint8_t *in1, const uint8_t *in2, uint8_t *out) {
|
||||||
|
for(int i=0;i<16;i++) {
|
||||||
|
out[i]=in1[i]^in2[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encrypts a plaintext using AES256.
|
* Encrypts a plaintext using AES256.
|
||||||
*/
|
*/
|
||||||
static inline void aes256_enc(const uint8_t *plainText, uint8_t *cipherText) {
|
static inline void aes256_enc(const uint8_t *in, uint8_t *out) {
|
||||||
m_global = _mm_loadu_si128(reinterpret_cast<const __m128i *>(plainText));
|
|
||||||
|
|
||||||
DO_ENC_BLOCK_256(m_global, key_schedule);
|
#ifndef DISABLE_AESNI
|
||||||
|
if(bHasAES)
|
||||||
|
return ni_aes256_enc(in, out);
|
||||||
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(cipherText), m_global);
|
memcpy(out,in,16);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
state_t *state=(state_t*)out;
|
||||||
* Encrypts a plaintext using AES128 with 2 rounds.
|
|
||||||
*/
|
|
||||||
static inline void aes128_enc(const uint8_t *plainText, uint8_t *cipherText) {
|
|
||||||
m_global = _mm_loadu_si128(reinterpret_cast<const __m128i *>(plainText));
|
|
||||||
|
|
||||||
// Uses the 2 round encryption innstead of the full 10 round encryption
|
uint8_t round = 0;
|
||||||
DO_ENC_BLOCK_2ROUND(m_global, key_schedule);
|
|
||||||
|
|
||||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(cipherText), m_global);
|
// Add the First round key to the state before starting the rounds.
|
||||||
|
AddRoundKey(0, state, RoundKey256);
|
||||||
|
|
||||||
|
// There will be Nr rounds.
|
||||||
|
// The first Nr-1 rounds are identical.
|
||||||
|
// These Nr-1 rounds are executed in the loop below.
|
||||||
|
for (round = 1; round < ENCRYPTNR256; ++round)
|
||||||
|
{
|
||||||
|
SubBytes(state);
|
||||||
|
ShiftRows(state);
|
||||||
|
MixColumns(state);
|
||||||
|
AddRoundKey(round, state, RoundKey256);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last round is given below.
|
||||||
|
// The MixColumns function is not here in the last round.
|
||||||
|
SubBytes(state);
|
||||||
|
ShiftRows(state);
|
||||||
|
AddRoundKey(ENCRYPTNR256, state, RoundKey256);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Encrypts an integer using AES128 with 2 rounds.
|
* Encrypts an integer using AES128 with 2 rounds.
|
||||||
*/
|
*/
|
||||||
static inline __m128i aes128_enc_int(__m128i plainText) {
|
static inline void aes128_enc(uint8_t *in, uint8_t *out) {
|
||||||
// Uses the 2 round encryption innstead of the full 10 round encryption
|
|
||||||
DO_ENC_BLOCK_2ROUND(plainText, key_schedule);
|
|
||||||
return plainText;
|
|
||||||
}
|
|
||||||
|
|
||||||
__m128i m1;
|
#ifndef DISABLE_AESNI
|
||||||
__m128i m2;
|
if(bHasAES)
|
||||||
__m128i m3;
|
return ni_aes128_enc(in, out);
|
||||||
__m128i m4;
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
|
memcpy(out,in,16);
|
||||||
|
|
||||||
|
state_t *state=(state_t*)out;
|
||||||
|
|
||||||
|
uint8_t round = 0;
|
||||||
|
|
||||||
|
// Add the First round key to the state before starting the rounds.
|
||||||
|
AddRoundKey(0, state, RoundKey128);
|
||||||
|
|
||||||
|
// There will be Nr rounds.
|
||||||
|
// The first Nr-1 rounds are identical.
|
||||||
|
// These Nr-1 rounds are executed in the loop below.
|
||||||
|
for (round = 1; round < ENCRYPTNR128; ++round)
|
||||||
|
{
|
||||||
|
SubBytes(state);
|
||||||
|
ShiftRows(state);
|
||||||
|
MixColumns(state);
|
||||||
|
AddRoundKey(round, state, RoundKey128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Uses AES cache mode to map a 2 block ciphertext into 128 bit result.
|
* Uses AES cache mode to map a 2 block ciphertext into 128 bit result.
|
||||||
*/
|
*/
|
||||||
static inline void aes128_2b(uint8_t *block1, uint8_t *block2, uint8_t *res) {
|
static inline void aes128_2b(uint8_t *block1, uint8_t *block2, uint8_t *res) {
|
||||||
m1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block1));
|
|
||||||
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block2));
|
#ifndef DISABLE_AESNI
|
||||||
m3 = aes128_enc_int(m1); // E(L)
|
if(bHasAES)
|
||||||
m3 = aes128_enc_int(_mm_xor_si128(m3, m2));
|
return ni_aes128_2b(block1, block2, res);
|
||||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(res), m3);
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
|
uint8_t m1[16];
|
||||||
|
uint8_t m2[16];
|
||||||
|
uint8_t m3[16];
|
||||||
|
uint8_t intermediate[16];
|
||||||
|
|
||||||
|
memcpy(m1,block1,16);
|
||||||
|
memcpy(m2,block2,16);
|
||||||
|
|
||||||
|
aes128_enc(m1,m3);
|
||||||
|
xor128(m3, m2, intermediate);
|
||||||
|
aes128_enc(intermediate,m3);
|
||||||
|
|
||||||
|
memcpy(res,m3,16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Uses AES cache mode to map a 3 block ciphertext into 128 bit result.
|
* Uses AES cache mode to map a 3 block ciphertext into 128 bit result.
|
||||||
*/
|
*/
|
||||||
static inline void aes128_3b(uint8_t *block1, uint8_t* block2, uint8_t *block3, uint8_t* res) {
|
static inline void aes128_3b(uint8_t *block1, uint8_t* block2, uint8_t *block3, uint8_t* res) {
|
||||||
m1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block1));
|
|
||||||
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block2));
|
|
||||||
|
|
||||||
m1 = aes128_enc_int(m1); // E(La)
|
#ifndef DISABLE_AESNI
|
||||||
m2 = aes128_enc_int(m2); // E(Ra)
|
if(bHasAES)
|
||||||
|
return ni_aes128_3b(block1, block2, block3, res);
|
||||||
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
m1 = _mm_xor_si128(m1, m2);
|
uint8_t m1[16];
|
||||||
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block3));
|
uint8_t m2[16];
|
||||||
|
uint8_t m3[16];
|
||||||
|
|
||||||
m2 = aes128_enc_int(m2);
|
memcpy(m1,block1,16);
|
||||||
m1 = _mm_xor_si128(m1, m2);
|
memcpy(m2,block2,16);
|
||||||
m3 = aes128_enc_int(m1);
|
|
||||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(res), m3);
|
aes128_enc(m1,m1); // E(La)
|
||||||
|
aes128_enc(m2,m2); // E(Ra)
|
||||||
|
|
||||||
|
xor128(m1, m2, m1);
|
||||||
|
memcpy(m2,block3,16);
|
||||||
|
|
||||||
|
aes128_enc(m2,m2);
|
||||||
|
xor128(m1, m2, m1);
|
||||||
|
aes128_enc(m1,m3);
|
||||||
|
memcpy(res,m3,16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Uses AES cache mode to map a 4 block ciphertext into 128 bit result.
|
* Uses AES cache mode to map a 4 block ciphertext into 128 bit result.
|
||||||
*/
|
*/
|
||||||
static inline void aes128_4b(uint8_t *block1, uint8_t* block2, uint8_t *block3, uint8_t* block4, uint8_t* res) {
|
static inline void aes128_4b(uint8_t *block1, uint8_t* block2, uint8_t *block3, uint8_t* block4, uint8_t* res) {
|
||||||
m1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block1));
|
|
||||||
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block3));
|
|
||||||
m3 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block2));
|
|
||||||
m4 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block4));
|
|
||||||
|
|
||||||
m1 = aes128_enc_int(m1); // E(La)
|
#ifndef DISABLE_AESNI
|
||||||
m1 = _mm_xor_si128(m1, m3);
|
if(bHasAES)
|
||||||
m1 = aes128_enc_int(m1); // E(E(La) ^ Lb)
|
return ni_aes128_4b(block1, block2, block3, block4, res);
|
||||||
m2 = aes128_enc_int(m2); // E(Ra)
|
#endif // DISABLE_AESNI
|
||||||
|
|
||||||
m1 = _mm_xor_si128(m1, m2); // xor e(Ra)
|
uint8_t m1[16];
|
||||||
m1 = _mm_xor_si128(m1, m4); // xor Rb
|
uint8_t m2[16];
|
||||||
|
uint8_t m3[16];
|
||||||
|
uint8_t m4[16];
|
||||||
|
|
||||||
m3 = aes128_enc_int(m1);
|
memcpy(m1,block1,16);
|
||||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(res), m3);
|
memcpy(m2,block2,16);
|
||||||
|
memcpy(m3,block3,16);
|
||||||
|
memcpy(m4,block4,16);
|
||||||
|
|
||||||
|
aes128_enc(m1,m1); // E(La)
|
||||||
|
xor128(m1, m3, m1);
|
||||||
|
aes128_enc(m1,m1); // E(E(La) ^ Lb)
|
||||||
|
aes128_enc(m2,m2); // E(Ra)
|
||||||
|
|
||||||
|
xor128(m1, m2, m1); // xor e(Ra)
|
||||||
|
xor128(m1, m4, m1); // xor Rb
|
||||||
|
|
||||||
|
aes128_enc(m1,m3); // E(La)
|
||||||
|
memcpy(res,m3,16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SRC_CPP_AES_HPP_
|
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
// Copyright 2018 Chia Network Inc
|
||||||
|
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Some public domain code is taken from pycrypto:
|
||||||
|
// https://github.com/dlitz/pycrypto/blob/master/src/AESNI.c
|
||||||
|
//
|
||||||
|
// AESNI.c: AES using AES-NI instructions
|
||||||
|
//
|
||||||
|
// Written in 2013 by Sebastian Ramacher <sebastian@ramacher.at>
|
||||||
|
|
||||||
|
#ifndef SRC_CPP_AES_HPP_
|
||||||
|
#define SRC_CPP_AES_HPP_
|
||||||
|
|
||||||
|
#include <string.h> // for memcmp
|
||||||
|
#include <wmmintrin.h> // for intrinsics for AES-NI
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a message of 128 bits with a 128 bit key, using
|
||||||
|
* 10 rounds of AES128 (9 full rounds and one final round). Uses AES-NI
|
||||||
|
* assembly instructions.
|
||||||
|
*/
|
||||||
|
#define DO_ENC_BLOCK_128(m, k) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
m = _mm_xor_si128(m, k[0]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[1]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[2]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[3]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[4]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[5]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[6]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[7]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[8]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[9]); \
|
||||||
|
m = _mm_aesenclast_si128(m, k[10]); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a message of 128 bits with a 256 bit key, using
|
||||||
|
* 13 rounds of AES256 (13 full rounds and one final round). Uses
|
||||||
|
* AES-NI assembly instructions.
|
||||||
|
*/
|
||||||
|
#define DO_ENC_BLOCK_256(m, k) \
|
||||||
|
do {\
|
||||||
|
m = _mm_xor_si128(m, k[ 0]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 1]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 2]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 3]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 4]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 5]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 6]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 7]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 8]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 9]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[ 10]);\
|
||||||
|
m = _mm_aesenc_si128(m, k[ 11]);\
|
||||||
|
m = _mm_aesenc_si128(m, k[ 12]);\
|
||||||
|
m = _mm_aesenc_si128(m, k[ 13]);\
|
||||||
|
m = _mm_aesenclast_si128(m, k[ 14]);\
|
||||||
|
}while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts a message of 128 bits with a 128 bit key, using
|
||||||
|
* 2 full rounds of AES128. Uses AES-NI assembly instructions.
|
||||||
|
*/
|
||||||
|
#define DO_ENC_BLOCK_2ROUND(m, k) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
m = _mm_xor_si128(m, k[0]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[1]); \
|
||||||
|
m = _mm_aesenc_si128(m, k[2]); \
|
||||||
|
} while (0)
|
||||||
|
/**
|
||||||
|
* Decrypts a ciphertext of 128 bits with a 128 bit key, using
|
||||||
|
* 10 rounds of AES128 (9 full rounds and one final round).
|
||||||
|
* Uses AES-NI assembly instructions.
|
||||||
|
*/
|
||||||
|
#define DO_DEC_BLOCK(m, k) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
m = _mm_xor_si128(m, k[10 + 0]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 1]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 2]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 3]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 4]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 5]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 6]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 7]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 8]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[10 + 9]); \
|
||||||
|
m = _mm_aesdeclast_si128(m, k[0]); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts a ciphertext of 128 bits with a 128 bit key, using
|
||||||
|
* 2 full rounds of AES128. Uses AES-NI assembly instructions.
|
||||||
|
* Will not work unless key schedule is modified.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
#define DO_DEC_BLOCK_2ROUND(m, k) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
m = _mm_xor_si128(m, k[2 + 0]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[2 + 1]); \
|
||||||
|
m = _mm_aesdec_si128(m, k[2 + 2]); \
|
||||||
|
} while (0)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static __m128i key_schedule[20]; // The expanded key
|
||||||
|
|
||||||
|
static __m128i aes128_keyexpand(__m128i key) {
|
||||||
|
key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
|
||||||
|
key = _mm_xor_si128(key, _mm_slli_si128(key, 4));
|
||||||
|
return _mm_xor_si128(key, _mm_slli_si128(key, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KEYEXP128_H(K1, K2, I, S) _mm_xor_si128(aes128_keyexpand(K1), \
|
||||||
|
_mm_shuffle_epi32(_mm_aeskeygenassist_si128(K2, I), S))
|
||||||
|
|
||||||
|
#define KEYEXP128(K, I) KEYEXP128_H(K, K, I, 0xff)
|
||||||
|
#define KEYEXP256(K1, K2, I) KEYEXP128_H(K1, K2, I, 0xff)
|
||||||
|
#define KEYEXP256_2(K1, K2) KEYEXP128_H(K1, K2, 0x00, 0xaa)
|
||||||
|
|
||||||
|
// public API
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads an AES key. Can either be a 16 byte or 32 byte bytearray.
|
||||||
|
*/
|
||||||
|
void ni_aes_load_key(uint8_t *enc_key, int keylen) {
|
||||||
|
switch (keylen) {
|
||||||
|
case 16: {
|
||||||
|
/* 128 bit key setup */
|
||||||
|
key_schedule[0] = _mm_loadu_si128((const __m128i*) enc_key);
|
||||||
|
key_schedule[1] = KEYEXP128(key_schedule[0], 0x01);
|
||||||
|
key_schedule[2] = KEYEXP128(key_schedule[1], 0x02);
|
||||||
|
key_schedule[3] = KEYEXP128(key_schedule[2], 0x04);
|
||||||
|
key_schedule[4] = KEYEXP128(key_schedule[3], 0x08);
|
||||||
|
key_schedule[5] = KEYEXP128(key_schedule[4], 0x10);
|
||||||
|
key_schedule[6] = KEYEXP128(key_schedule[5], 0x20);
|
||||||
|
key_schedule[7] = KEYEXP128(key_schedule[6], 0x40);
|
||||||
|
key_schedule[8] = KEYEXP128(key_schedule[7], 0x80);
|
||||||
|
key_schedule[9] = KEYEXP128(key_schedule[8], 0x1B);
|
||||||
|
key_schedule[10] = KEYEXP128(key_schedule[9], 0x36);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 32: {
|
||||||
|
/* 256 bit key setup */
|
||||||
|
key_schedule[0] = _mm_loadu_si128((const __m128i*) enc_key);
|
||||||
|
key_schedule[1] = _mm_loadu_si128((const __m128i*) (enc_key+16));
|
||||||
|
key_schedule[2] = KEYEXP256(key_schedule[0], key_schedule[1], 0x01);
|
||||||
|
key_schedule[3] = KEYEXP256_2(key_schedule[1], key_schedule[2]);
|
||||||
|
key_schedule[4] = KEYEXP256(key_schedule[2], key_schedule[3], 0x02);
|
||||||
|
key_schedule[5] = KEYEXP256_2(key_schedule[3], key_schedule[4]);
|
||||||
|
key_schedule[6] = KEYEXP256(key_schedule[4], key_schedule[5], 0x04);
|
||||||
|
key_schedule[7] = KEYEXP256_2(key_schedule[5], key_schedule[6]);
|
||||||
|
key_schedule[8] = KEYEXP256(key_schedule[6], key_schedule[7], 0x08);
|
||||||
|
key_schedule[9] = KEYEXP256_2(key_schedule[7], key_schedule[8]);
|
||||||
|
key_schedule[10] = KEYEXP256(key_schedule[8], key_schedule[9], 0x10);
|
||||||
|
key_schedule[11] = KEYEXP256_2(key_schedule[9], key_schedule[10]);
|
||||||
|
key_schedule[12] = KEYEXP256(key_schedule[10], key_schedule[11], 0x20);
|
||||||
|
key_schedule[13] = KEYEXP256_2(key_schedule[11], key_schedule[12]);
|
||||||
|
key_schedule[14] = KEYEXP256(key_schedule[12], key_schedule[13], 0x40);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declares a global variable for efficiency.
|
||||||
|
__m128i m_global;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encrypts a plaintext using AES256.
|
||||||
|
*/
|
||||||
|
static inline void ni_aes256_enc(const uint8_t *plainText, uint8_t *cipherText) {
|
||||||
|
m_global = _mm_loadu_si128(reinterpret_cast<const __m128i *>(plainText));
|
||||||
|
|
||||||
|
DO_ENC_BLOCK_256(m_global, key_schedule);
|
||||||
|
|
||||||
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(cipherText), m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encrypts a plaintext using AES128 with 2 rounds.
|
||||||
|
*/
|
||||||
|
static inline void ni_aes128_enc(const uint8_t *plainText, uint8_t *cipherText) {
|
||||||
|
m_global = _mm_loadu_si128(reinterpret_cast<const __m128i *>(plainText));
|
||||||
|
|
||||||
|
// Uses the 2 round encryption innstead of the full 10 round encryption
|
||||||
|
DO_ENC_BLOCK_2ROUND(m_global, key_schedule);
|
||||||
|
|
||||||
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(cipherText), m_global);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encrypts an integer using AES128 with 2 rounds.
|
||||||
|
*/
|
||||||
|
static inline __m128i ni_aes128_enc_int(__m128i plainText) {
|
||||||
|
// Uses the 2 round encryption innstead of the full 10 round encryption
|
||||||
|
DO_ENC_BLOCK_2ROUND(plainText, key_schedule);
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128i m1;
|
||||||
|
__m128i m2;
|
||||||
|
__m128i m3;
|
||||||
|
__m128i m4;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uses AES cache mode to map a 2 block ciphertext into 128 bit result.
|
||||||
|
*/
|
||||||
|
static inline void ni_aes128_2b(uint8_t *block1, uint8_t *block2, uint8_t *res) {
|
||||||
|
m1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block1));
|
||||||
|
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block2));
|
||||||
|
m3 = ni_aes128_enc_int(m1); // E(L)
|
||||||
|
m3 = ni_aes128_enc_int(_mm_xor_si128(m3, m2));
|
||||||
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(res), m3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uses AES cache mode to map a 3 block ciphertext into 128 bit result.
|
||||||
|
*/
|
||||||
|
static inline void ni_aes128_3b(uint8_t *block1, uint8_t* block2, uint8_t *block3, uint8_t* res) {
|
||||||
|
m1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block1));
|
||||||
|
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block2));
|
||||||
|
|
||||||
|
m1 = ni_aes128_enc_int(m1); // E(La)
|
||||||
|
m2 = ni_aes128_enc_int(m2); // E(Ra)
|
||||||
|
|
||||||
|
m1 = _mm_xor_si128(m1, m2);
|
||||||
|
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block3));
|
||||||
|
|
||||||
|
m2 = ni_aes128_enc_int(m2);
|
||||||
|
m1 = _mm_xor_si128(m1, m2);
|
||||||
|
m3 = ni_aes128_enc_int(m1);
|
||||||
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(res), m3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uses AES cache mode to map a 4 block ciphertext into 128 bit result.
|
||||||
|
*/
|
||||||
|
static inline void ni_aes128_4b(uint8_t *block1, uint8_t* block2, uint8_t *block3, uint8_t* block4, uint8_t* res) {
|
||||||
|
m1 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block1));
|
||||||
|
m2 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block3));
|
||||||
|
m3 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block2));
|
||||||
|
m4 = _mm_loadu_si128(reinterpret_cast<__m128i *>(block4));
|
||||||
|
|
||||||
|
m1 = ni_aes128_enc_int(m1); // E(La)
|
||||||
|
m1 = _mm_xor_si128(m1, m3);
|
||||||
|
m1 = ni_aes128_enc_int(m1); // E(E(La) ^ Lb)
|
||||||
|
m2 = ni_aes128_enc_int(m2); // E(Ra)
|
||||||
|
|
||||||
|
m1 = _mm_xor_si128(m1, m2); // xor e(Ra)
|
||||||
|
m1 = _mm_xor_si128(m1, m4); // xor Rb
|
||||||
|
|
||||||
|
m3 = ni_aes128_enc_int(m1);
|
||||||
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(res), m3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SRC_CPP_AES_HPP_
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "aes.hpp"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uint8_t enc_key[32];
|
||||||
|
uint8_t in[16];
|
||||||
|
uint8_t out[16];
|
||||||
|
|
||||||
|
memset(enc_key,0x00,sizeof(enc_key));
|
||||||
|
memset(in,0x00,sizeof(in));
|
||||||
|
|
||||||
|
aes_load_key(enc_key, sizeof(enc_key));
|
||||||
|
aes256_enc(in, out);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ yarl==1.4.2
|
||||||
zipp==2.0.0
|
zipp==2.0.0
|
||||||
sortedcontainers==2.1.0
|
sortedcontainers==2.1.0
|
||||||
-e lib/chiapos
|
-e lib/chiapos
|
||||||
|
-e lib/bip158
|
||||||
-e lib/py-setproctitle
|
-e lib/py-setproctitle
|
||||||
-e lib/chiavdf/fast_vdf
|
-e lib/chiavdf/fast_vdf
|
||||||
-e git+https://github.com/Chia-Network/clvm.git@134c2f92d575a542272ce0cbe59c167b255e50ae#egg=clvm
|
-e git+https://github.com/Chia-Network/clvm.git@bb538804062c01f999a228c4fc1e17a6e2835851#egg=clvm
|
||||||
-e git+https://github.com/Chia-Network/clvm_tools.git@208dc0bbf41e16d8e5dd8cbc1510e1528a48bbe1#egg=clvm_tools
|
-e git+https://github.com/Chia-Network/clvm_tools.git@343a82f16f3da1d042283f9f8969315e0c71bcf5#egg=clvm_tools
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -4,7 +4,7 @@ from typing import Any, Dict, List, Set
|
||||||
from blspy import PrivateKey, Util
|
from blspy import PrivateKey, Util
|
||||||
|
|
||||||
from src.consensus.block_rewards import calculate_block_reward
|
from src.consensus.block_rewards import calculate_block_reward
|
||||||
from src.consensus.constants import constants
|
from src.consensus.constants import constants as consensus_constants
|
||||||
from src.consensus.pot_iterations import calculate_iterations_quality
|
from src.consensus.pot_iterations import calculate_iterations_quality
|
||||||
from src.pool import create_coinbase_coin_and_signature
|
from src.pool import create_coinbase_coin_and_signature
|
||||||
from src.protocols import farmer_protocol, harvester_protocol
|
from src.protocols import farmer_protocol, harvester_protocol
|
||||||
|
@ -23,7 +23,7 @@ HARVESTER PROTOCOL (FARMER <-> HARVESTER)
|
||||||
|
|
||||||
|
|
||||||
class Farmer:
|
class Farmer:
|
||||||
def __init__(self, farmer_config: Dict, key_config: Dict):
|
def __init__(self, farmer_config: Dict, key_config: Dict, override_constants={}):
|
||||||
self.config = farmer_config
|
self.config = farmer_config
|
||||||
self.key_config = key_config
|
self.key_config = key_config
|
||||||
self.harvester_responses_header_hash: Dict[bytes32, bytes32] = {}
|
self.harvester_responses_header_hash: Dict[bytes32, bytes32] = {}
|
||||||
|
@ -39,6 +39,9 @@ class Farmer:
|
||||||
self.current_weight: uint64 = uint64(0)
|
self.current_weight: uint64 = uint64(0)
|
||||||
self.coinbase_rewards: Dict[uint32, Any] = {}
|
self.coinbase_rewards: Dict[uint32, Any] = {}
|
||||||
self.proof_of_time_estimate_ips: uint64 = uint64(10000)
|
self.proof_of_time_estimate_ips: uint64 = uint64(10000)
|
||||||
|
self.constants = consensus_constants.copy()
|
||||||
|
for key, value in override_constants.items():
|
||||||
|
self.constants[key] = value
|
||||||
|
|
||||||
async def _on_connect(self):
|
async def _on_connect(self):
|
||||||
# Sends a handshake to the harvester
|
# Sends a handshake to the harvester
|
||||||
|
@ -81,7 +84,7 @@ class Farmer:
|
||||||
challenge_response.plot_size,
|
challenge_response.plot_size,
|
||||||
difficulty,
|
difficulty,
|
||||||
self.proof_of_time_estimate_ips,
|
self.proof_of_time_estimate_ips,
|
||||||
constants["MIN_BLOCK_TIME"],
|
self.constants["MIN_BLOCK_TIME"],
|
||||||
)
|
)
|
||||||
if height < 1000: # As the difficulty adjusts, don't fetch all qualities
|
if height < 1000: # As the difficulty adjusts, don't fetch all qualities
|
||||||
if challenge_response.challenge_hash not in self.challenge_to_best_iters:
|
if challenge_response.challenge_hash not in self.challenge_to_best_iters:
|
||||||
|
@ -160,7 +163,7 @@ class Farmer:
|
||||||
response.proof.size,
|
response.proof.size,
|
||||||
difficulty,
|
difficulty,
|
||||||
self.proof_of_time_estimate_ips,
|
self.proof_of_time_estimate_ips,
|
||||||
constants["MIN_BLOCK_TIME"],
|
self.constants["MIN_BLOCK_TIME"],
|
||||||
)
|
)
|
||||||
estimate_secs: float = number_iters / self.proof_of_time_estimate_ips
|
estimate_secs: float = number_iters / self.proof_of_time_estimate_ips
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,15 @@ from src.consensus.pot_iterations import (
|
||||||
calculate_ips_from_iterations,
|
calculate_ips_from_iterations,
|
||||||
calculate_iterations_quality,
|
calculate_iterations_quality,
|
||||||
)
|
)
|
||||||
from src.store import FullNodeStore
|
from src.full_node.store import FullNodeStore
|
||||||
|
|
||||||
from src.types.full_block import FullBlock, additions_for_npc
|
from src.types.full_block import FullBlock, additions_for_npc
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
from src.types.header_block import HeaderBlock
|
from src.types.header_block import HeaderBlock
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from src.util.ConsensusError import Err
|
from src.util.ConsensusError import Err
|
||||||
from src.util.blockchain_check_conditions import blockchain_check_conditions_dict
|
from src.util.blockchain_check_conditions import blockchain_check_conditions_dict
|
||||||
from src.util.condition_tools import hash_key_pairs_for_conditions_dict
|
from src.util.condition_tools import hash_key_pairs_for_conditions_dict
|
||||||
|
@ -82,7 +82,7 @@ class Blockchain:
|
||||||
in the consensus constants config.
|
in the consensus constants config.
|
||||||
"""
|
"""
|
||||||
self = Blockchain()
|
self = Blockchain()
|
||||||
self.constants = consensus_constants
|
self.constants = consensus_constants.copy()
|
||||||
for key, value in override_constants.items():
|
for key, value in override_constants.items():
|
||||||
self.constants[key] = value
|
self.constants[key] = value
|
||||||
self.tips = []
|
self.tips = []
|
||||||
|
@ -744,8 +744,7 @@ class Blockchain:
|
||||||
pool.shutdown(wait=True)
|
pool.shutdown(wait=True)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@staticmethod
|
def pre_validate_block_multi(self, data) -> Tuple[bool, Optional[bytes]]:
|
||||||
def pre_validate_block_multi(data) -> Tuple[bool, Optional[bytes]]:
|
|
||||||
"""
|
"""
|
||||||
Validates all parts of FullBlock that don't need to be serially checked
|
Validates all parts of FullBlock that don't need to be serially checked
|
||||||
"""
|
"""
|
||||||
|
@ -755,9 +754,7 @@ class Blockchain:
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
# 4. Check PoT
|
# 4. Check PoT
|
||||||
if not block.proof_of_time.is_valid(
|
if not block.proof_of_time.is_valid(self.constants["DISCRIMINANT_SIZE_BITS"]):
|
||||||
consensus_constants["DISCRIMINANT_SIZE_BITS"]
|
|
||||||
):
|
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
# 9. Check harvester signature of header data is valid based on harvester key
|
# 9. Check harvester signature of header data is valid based on harvester key
|
||||||
|
@ -927,9 +924,7 @@ class Blockchain:
|
||||||
if not block.body.transactions:
|
if not block.body.transactions:
|
||||||
return Err.UNKNOWN
|
return Err.UNKNOWN
|
||||||
# Get List of names removed, puzzles hashes for removed coins and conditions crated
|
# Get List of names removed, puzzles hashes for removed coins and conditions crated
|
||||||
error, npc_list, cost = await get_name_puzzle_conditions(
|
error, npc_list, cost = get_name_puzzle_conditions(block.body.transactions)
|
||||||
block.body.transactions
|
|
||||||
)
|
|
||||||
|
|
||||||
if cost > 6000:
|
if cost > 6000:
|
||||||
return Err.BLOCK_COST_EXCEEDS_MAX
|
return Err.BLOCK_COST_EXCEEDS_MAX
|
||||||
|
@ -953,7 +948,7 @@ class Blockchain:
|
||||||
# Check additions for max coin amount
|
# Check additions for max coin amount
|
||||||
for coin in additions:
|
for coin in additions:
|
||||||
additions_dic[coin.name()] = coin
|
additions_dic[coin.name()] = coin
|
||||||
if coin.amount >= consensus_constants["MAX_COIN_AMOUNT"]:
|
if coin.amount >= self.constants["MAX_COIN_AMOUNT"]:
|
||||||
return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
|
return Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
|
||||||
|
|
||||||
# Watch out for duplicate outputs
|
# Watch out for duplicate outputs
|
||||||
|
@ -1008,7 +1003,6 @@ class Blockchain:
|
||||||
|
|
||||||
# Check coinbase reward
|
# Check coinbase reward
|
||||||
if fees + fee_base != block.body.fees_coin.amount:
|
if fees + fee_base != block.body.fees_coin.amount:
|
||||||
print("Fees,", fees, fee_base, block.body.fees_coin.amount)
|
|
||||||
return Err.BAD_COINBASE_REWARD
|
return Err.BAD_COINBASE_REWARD
|
||||||
|
|
||||||
# Verify that removed coin puzzle_hashes match with calculated puzzle_hashes
|
# Verify that removed coin puzzle_hashes match with calculated puzzle_hashes
|
|
@ -3,8 +3,8 @@ from typing import Dict, Optional, List
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.util.ints import uint32
|
from src.util.ints import uint32
|
|
@ -3,38 +3,39 @@ import concurrent
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from asyncio import Event
|
from asyncio import Event
|
||||||
from secrets import token_bytes
|
|
||||||
from typing import AsyncGenerator, List, Optional, Tuple, Dict
|
from typing import AsyncGenerator, List, Optional, Tuple, Dict
|
||||||
|
|
||||||
|
from chiabip158 import PyBIP158
|
||||||
from chiapos import Verifier
|
from chiapos import Verifier
|
||||||
|
|
||||||
import src.protocols.wallet_protocol
|
import src.protocols.wallet_protocol
|
||||||
from src.blockchain import Blockchain, ReceiveBlockResult
|
from src.full_node.blockchain import Blockchain, ReceiveBlockResult
|
||||||
from src.consensus.block_rewards import calculate_base_fee
|
from src.consensus.block_rewards import calculate_base_fee
|
||||||
from src.consensus.constants import constants
|
from src.consensus.constants import constants as consensus_constants
|
||||||
from src.consensus.pot_iterations import calculate_iterations
|
from src.consensus.pot_iterations import calculate_iterations
|
||||||
from src.consensus.weight_verifier import verify_weight
|
from src.consensus.weight_verifier import verify_weight
|
||||||
from src.protocols.wallet_protocol import FullProofForHash, ProofHash
|
from src.protocols.wallet_protocol import FullProofForHash, ProofHash
|
||||||
from src.store import FullNodeStore
|
from src.full_node.store import FullNodeStore
|
||||||
from src.protocols import farmer_protocol, full_node_protocol, timelord_protocol
|
from src.protocols import farmer_protocol, full_node_protocol, timelord_protocol
|
||||||
|
from src.util.merkle_set import MerkleSet
|
||||||
from src.util.bundle_tools import best_solution_program
|
from src.util.bundle_tools import best_solution_program
|
||||||
from src.mempool_manager import MempoolManager
|
from src.full_node.mempool_manager import MempoolManager
|
||||||
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
|
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
|
||||||
from src.server.server import ChiaServer
|
from src.server.server import ChiaServer
|
||||||
from src.types.body import Body
|
from src.types.body import Body
|
||||||
from src.types.challenge import Challenge
|
from src.types.challenge import Challenge
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin, hash_coin_list
|
||||||
from src.types.hashable.BLSSignature import BLSSignature
|
from src.types.hashable.BLSSignature import BLSSignature
|
||||||
from src.util.hash import std_hash
|
from src.util.hash import std_hash
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.header import Header, HeaderData
|
from src.types.header import Header, HeaderData
|
||||||
from src.types.header_block import HeaderBlock
|
from src.types.header_block import HeaderBlock
|
||||||
from src.types.peer_info import PeerInfo
|
from src.types.peer_info import PeerInfo
|
||||||
from src.types.proof_of_space import ProofOfSpace
|
from src.types.proof_of_space import ProofOfSpace
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from src.util import errors
|
from src.util import errors
|
||||||
from src.util.api_decorators import api_request
|
from src.util.api_decorators import api_request
|
||||||
from src.util.errors import InvalidUnfinishedBlock
|
from src.util.errors import InvalidUnfinishedBlock
|
||||||
|
@ -52,7 +53,9 @@ class FullNode:
|
||||||
mempool_manager: MempoolManager,
|
mempool_manager: MempoolManager,
|
||||||
unspent_store: CoinStore,
|
unspent_store: CoinStore,
|
||||||
name: str = None,
|
name: str = None,
|
||||||
|
override_constants={},
|
||||||
):
|
):
|
||||||
|
|
||||||
self.config: Dict = config
|
self.config: Dict = config
|
||||||
self.store: FullNodeStore = store
|
self.store: FullNodeStore = store
|
||||||
self.blockchain: Blockchain = blockchain
|
self.blockchain: Blockchain = blockchain
|
||||||
|
@ -60,6 +63,9 @@ class FullNode:
|
||||||
self._shut_down = False # Set to true to close all infinite loops
|
self._shut_down = False # Set to true to close all infinite loops
|
||||||
self.server: Optional[ChiaServer] = None
|
self.server: Optional[ChiaServer] = None
|
||||||
self.unspent_store: CoinStore = unspent_store
|
self.unspent_store: CoinStore = unspent_store
|
||||||
|
self.constants = consensus_constants.copy()
|
||||||
|
for key, value in override_constants.items():
|
||||||
|
self.constants[key] = value
|
||||||
if name:
|
if name:
|
||||||
self.log = logging.getLogger(name)
|
self.log = logging.getLogger(name)
|
||||||
else:
|
else:
|
||||||
|
@ -934,11 +940,6 @@ class FullNode:
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
assert challenge is not None
|
assert challenge is not None
|
||||||
print(self.store.unfinished_blocks.keys())
|
|
||||||
print(
|
|
||||||
"It's none..",
|
|
||||||
(challenge.get_hash(), new_unfinished_block.number_of_iterations),
|
|
||||||
)
|
|
||||||
yield OutboundMessage(
|
yield OutboundMessage(
|
||||||
NodeType.FULL_NODE,
|
NodeType.FULL_NODE,
|
||||||
Message(
|
Message(
|
||||||
|
@ -1030,7 +1031,7 @@ class FullNode:
|
||||||
int(iterations_needed / (self.store.get_proof_of_time_estimate_ips()))
|
int(iterations_needed / (self.store.get_proof_of_time_estimate_ips()))
|
||||||
)
|
)
|
||||||
|
|
||||||
if expected_time > constants["PROPAGATION_DELAY_THRESHOLD"]:
|
if expected_time > self.constants["PROPAGATION_DELAY_THRESHOLD"]:
|
||||||
self.log.info(f"Block is slow, expected {expected_time} seconds, waiting")
|
self.log.info(f"Block is slow, expected {expected_time} seconds, waiting")
|
||||||
# If this block is slow, sleep to allow faster blocks to come out first
|
# If this block is slow, sleep to allow faster blocks to come out first
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
@ -1043,7 +1044,7 @@ class FullNode:
|
||||||
# If this is the first block we see at this height, propagate
|
# If this is the first block we see at this height, propagate
|
||||||
self.store.set_unfinished_block_leader((block.height, expected_time))
|
self.store.set_unfinished_block_leader((block.height, expected_time))
|
||||||
elif block.height == leader[0]:
|
elif block.height == leader[0]:
|
||||||
if expected_time > leader[1] + constants["PROPAGATION_THRESHOLD"]:
|
if expected_time > leader[1] + self.constants["PROPAGATION_THRESHOLD"]:
|
||||||
# If VDF is expected to finish X seconds later than the best, don't propagate
|
# If VDF is expected to finish X seconds later than the best, don't propagate
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"VDF will finish too late {expected_time} seconds, so don't propagate"
|
f"VDF will finish too late {expected_time} seconds, so don't propagate"
|
||||||
|
@ -1245,8 +1246,21 @@ class FullNode:
|
||||||
prev_header_hash: bytes32 = target_tip.get_hash()
|
prev_header_hash: bytes32 = target_tip.get_hash()
|
||||||
timestamp: uint64 = uint64(int(time.time()))
|
timestamp: uint64 = uint64(int(time.time()))
|
||||||
|
|
||||||
# TODO(straya): use a real BIP158 filter based on transactions
|
# Create filter
|
||||||
filter_hash: bytes32 = token_bytes(32)
|
byte_array_tx: List[bytes32] = []
|
||||||
|
if spend_bundle:
|
||||||
|
additions: List[Coin] = spend_bundle.additions()
|
||||||
|
removals: List[Coin] = spend_bundle.removals()
|
||||||
|
for coin in additions:
|
||||||
|
byte_array_tx.append(bytearray(coin.puzzle_hash))
|
||||||
|
for coin in removals:
|
||||||
|
byte_array_tx.append(bytearray(coin.name()))
|
||||||
|
byte_array_tx.append(bytearray(request.coinbase.puzzle_hash))
|
||||||
|
byte_array_tx.append(bytearray(fees_coin.puzzle_hash))
|
||||||
|
|
||||||
|
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||||
|
encoded_filter = bytes(bip158.GetEncoded())
|
||||||
|
|
||||||
proof_of_space_hash: bytes32 = request.proof_of_space.get_hash()
|
proof_of_space_hash: bytes32 = request.proof_of_space.get_hash()
|
||||||
body_hash: Body = body.get_hash()
|
body_hash: Body = body.get_hash()
|
||||||
difficulty = self.blockchain.get_next_difficulty(target_tip.header_hash)
|
difficulty = self.blockchain.get_next_difficulty(target_tip.header_hash)
|
||||||
|
@ -1255,16 +1269,50 @@ class FullNode:
|
||||||
vdf_ips: uint64 = self.blockchain.get_next_ips(target_tip_block)
|
vdf_ips: uint64 = self.blockchain.get_next_ips(target_tip_block)
|
||||||
|
|
||||||
iterations_needed: uint64 = calculate_iterations(
|
iterations_needed: uint64 = calculate_iterations(
|
||||||
request.proof_of_space, difficulty, vdf_ips, constants["MIN_BLOCK_TIME"],
|
request.proof_of_space,
|
||||||
|
difficulty,
|
||||||
|
vdf_ips,
|
||||||
|
self.constants["MIN_BLOCK_TIME"],
|
||||||
)
|
)
|
||||||
additions_root = token_bytes(32) # TODO(straya)
|
|
||||||
removal_root = token_bytes(32) # TODO(straya)
|
removal_merkle_set = MerkleSet()
|
||||||
|
addition_merkle_set = MerkleSet()
|
||||||
|
|
||||||
|
additions = []
|
||||||
|
removals = []
|
||||||
|
|
||||||
|
if spend_bundle:
|
||||||
|
additions = spend_bundle.additions()
|
||||||
|
removals = spend_bundle.removals()
|
||||||
|
|
||||||
|
additions.append(request.coinbase)
|
||||||
|
additions.append(fees_coin)
|
||||||
|
|
||||||
|
# Create removal Merkle set
|
||||||
|
for coin in removals:
|
||||||
|
removal_merkle_set.add_already_hashed(coin.name())
|
||||||
|
|
||||||
|
# Create addition Merkle set
|
||||||
|
puzzlehash_coins_map: Dict[bytes32, List[Coin]] = {}
|
||||||
|
for coin in additions:
|
||||||
|
if coin.puzzle_hash in puzzlehash_coins_map:
|
||||||
|
puzzlehash_coins_map[coin.puzzle_hash].append(coin)
|
||||||
|
else:
|
||||||
|
puzzlehash_coins_map[coin.puzzle_hash] = [coin]
|
||||||
|
|
||||||
|
# Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash
|
||||||
|
for puzzle, coins in puzzlehash_coins_map.items():
|
||||||
|
addition_merkle_set.add_already_hashed(puzzle)
|
||||||
|
addition_merkle_set.add_already_hashed(hash_coin_list(coins))
|
||||||
|
|
||||||
|
additions_root = addition_merkle_set.get_root()
|
||||||
|
removal_root = removal_merkle_set.get_root()
|
||||||
|
|
||||||
block_header_data: HeaderData = HeaderData(
|
block_header_data: HeaderData = HeaderData(
|
||||||
uint32(target_tip.height + 1),
|
uint32(target_tip.height + 1),
|
||||||
prev_header_hash,
|
prev_header_hash,
|
||||||
timestamp,
|
timestamp,
|
||||||
filter_hash,
|
encoded_filter,
|
||||||
proof_of_space_hash,
|
proof_of_space_hash,
|
||||||
body_hash,
|
body_hash,
|
||||||
target_tip.weight + difficulty,
|
target_tip.weight + difficulty,
|
||||||
|
@ -1648,3 +1696,25 @@ class FullNode:
|
||||||
yield OutboundMessage(
|
yield OutboundMessage(
|
||||||
NodeType.WALLET, Message("full_proof_for_hash", proof), Delivery.RESPOND
|
NodeType.WALLET, Message("full_proof_for_hash", proof), Delivery.RESPOND
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def request_additions(
|
||||||
|
self, request: src.protocols.wallet_protocol.RequestAdditions
|
||||||
|
) -> OutboundMessageGenerator:
|
||||||
|
block: Optional[FullBlock] = await self.store.get_block(request.header_hash)
|
||||||
|
if block:
|
||||||
|
additions = block.additions()
|
||||||
|
response = src.protocols.wallet_protocol.Additions(
|
||||||
|
block.height, block.header_hash, additions
|
||||||
|
)
|
||||||
|
yield OutboundMessage(
|
||||||
|
NodeType.WALLET,
|
||||||
|
Message("response_additions", response),
|
||||||
|
Delivery.BROADCAST,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
yield OutboundMessage(
|
||||||
|
NodeType.WALLET,
|
||||||
|
Message("response_reject_additions", request),
|
||||||
|
Delivery.BROADCAST,
|
||||||
|
)
|
|
@ -2,7 +2,7 @@ from typing import List, Dict
|
||||||
|
|
||||||
from sortedcontainers import SortedDict
|
from sortedcontainers import SortedDict
|
||||||
|
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.mempool_item import MempoolItem
|
from src.types.mempool_item import MempoolItem
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.ints import uint32, uint64
|
from src.util.ints import uint32, uint64
|
|
@ -5,14 +5,14 @@ import logging
|
||||||
from src.consensus.constants import constants as consensus_constants
|
from src.consensus.constants import constants as consensus_constants
|
||||||
from src.util.bundle_tools import best_solution_program
|
from src.util.bundle_tools import best_solution_program
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.types.mempool_item import MempoolItem
|
from src.types.mempool_item import MempoolItem
|
||||||
from src.mempool import Mempool
|
from src.full_node.mempool import Mempool
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from src.util.ConsensusError import Err
|
from src.util.ConsensusError import Err
|
||||||
from src.util.mempool_check_conditions import (
|
from src.util.mempool_check_conditions import (
|
||||||
get_name_puzzle_conditions,
|
get_name_puzzle_conditions,
|
||||||
|
@ -29,7 +29,7 @@ log = logging.getLogger(__name__)
|
||||||
class MempoolManager:
|
class MempoolManager:
|
||||||
def __init__(self, unspent_store: CoinStore, override_constants: Dict = {}):
|
def __init__(self, unspent_store: CoinStore, override_constants: Dict = {}):
|
||||||
# Allow passing in custom overrides
|
# Allow passing in custom overrides
|
||||||
self.constants: Dict = consensus_constants
|
self.constants: Dict = consensus_constants.copy()
|
||||||
for key, value in override_constants.items():
|
for key, value in override_constants.items():
|
||||||
self.constants[key] = value
|
self.constants[key] = value
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ class MempoolManager:
|
||||||
self.old_mempools: SortedDict[uint32, Dict[bytes32, MempoolItem]] = SortedDict()
|
self.old_mempools: SortedDict[uint32, Dict[bytes32, MempoolItem]] = SortedDict()
|
||||||
self.unspent_store = unspent_store
|
self.unspent_store = unspent_store
|
||||||
|
|
||||||
tx_per_sec = consensus_constants["TX_PER_SEC"]
|
tx_per_sec = self.constants["TX_PER_SEC"]
|
||||||
sec_per_block = consensus_constants["BLOCK_TIME_TARGET"]
|
sec_per_block = self.constants["BLOCK_TIME_TARGET"]
|
||||||
block_buffer_count = consensus_constants["MEMPOOL_BLOCK_BUFFER"]
|
block_buffer_count = self.constants["MEMPOOL_BLOCK_BUFFER"]
|
||||||
|
|
||||||
# MEMPOOL_SIZE = 60000
|
# MEMPOOL_SIZE = 60000
|
||||||
self.mempool_size = tx_per_sec * sec_per_block * block_buffer_count
|
self.mempool_size = tx_per_sec * sec_per_block * block_buffer_count
|
||||||
|
@ -101,7 +101,7 @@ class MempoolManager:
|
||||||
# Calculate the cost and fees
|
# Calculate the cost and fees
|
||||||
program = best_solution_program(new_spend)
|
program = best_solution_program(new_spend)
|
||||||
# npc contains names of the coins removed, puzzle_hashes and their spend conditions
|
# npc contains names of the coins removed, puzzle_hashes and their spend conditions
|
||||||
fail_reason, npc_list, cost = await get_name_puzzle_conditions(program)
|
fail_reason, npc_list, cost = get_name_puzzle_conditions(program)
|
||||||
if fail_reason:
|
if fail_reason:
|
||||||
return None, fail_reason
|
return None, fail_reason
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ class MempoolManager:
|
||||||
|
|
||||||
# Check additions for max coin amount
|
# Check additions for max coin amount
|
||||||
for coin in additions:
|
for coin in additions:
|
||||||
if coin.amount >= consensus_constants["MAX_COIN_AMOUNT"]:
|
if coin.amount >= self.constants["MAX_COIN_AMOUNT"]:
|
||||||
return None, Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
|
return None, Err.COIN_AMOUNT_EXCEEDS_MAXIMUM
|
||||||
|
|
||||||
# Watch out for duplicate outputs
|
# Watch out for duplicate outputs
|
|
@ -2,7 +2,7 @@ import blspy
|
||||||
|
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.BLSSignature import BLSSignature, BLSPublicKey
|
from src.types.hashable.BLSSignature import BLSSignature, BLSPublicKey
|
||||||
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
|
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from blspy import PrependSignature
|
from blspy import PrependSignature
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.BLSSignature import BLSSignature
|
from src.types.hashable.BLSSignature import BLSSignature
|
||||||
from src.types.proof_of_space import ProofOfSpace
|
from src.types.proof_of_space import ProofOfSpace
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
|
|
|
@ -2,7 +2,7 @@ from dataclasses import dataclass
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.header_block import HeaderBlock
|
from src.types.header_block import HeaderBlock
|
||||||
from src.types.peer_info import PeerInfo
|
from src.types.peer_info import PeerInfo
|
||||||
from src.types.proof_of_time import ProofOfTime
|
from src.types.proof_of_time import ProofOfTime
|
||||||
|
|
|
@ -5,7 +5,7 @@ from src.types.sized_bytes import bytes32
|
||||||
from src.util.cbor_message import cbor_message
|
from src.util.cbor_message import cbor_message
|
||||||
from src.util.ints import uint16
|
from src.util.ints import uint16
|
||||||
|
|
||||||
protocol_version = "0.0.5"
|
protocol_version = "0.0.6"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Handshake when establishing a connection between two servers.
|
Handshake when establishing a connection between two servers.
|
||||||
|
|
|
@ -2,7 +2,8 @@ from dataclasses import dataclass
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from src.types.body import Body
|
from src.types.body import Body
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.coin import Coin
|
||||||
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.header_block import HeaderBlock
|
from src.types.header_block import HeaderBlock
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.cbor_message import cbor_message
|
from src.util.cbor_message import cbor_message
|
||||||
|
@ -86,3 +87,33 @@ class FullProofForHash:
|
||||||
@cbor_message
|
@cbor_message
|
||||||
class ProofHash:
|
class ProofHash:
|
||||||
proof_hash: bytes32
|
proof_hash: bytes32
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
@cbor_message
|
||||||
|
class RequestAdditions:
|
||||||
|
height: uint32
|
||||||
|
header_hash: bytes32
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
@cbor_message
|
||||||
|
class RequestRemovals:
|
||||||
|
height: uint32
|
||||||
|
header_hash: bytes32
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
@cbor_message
|
||||||
|
class Additions:
|
||||||
|
height: uint32
|
||||||
|
header_hash: bytes32
|
||||||
|
coins: List[Coin]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
@cbor_message
|
||||||
|
class Removals:
|
||||||
|
height: uint32
|
||||||
|
header_hash: bytes32
|
||||||
|
coins: List[Coin]
|
||||||
|
|
|
@ -7,7 +7,7 @@ from src.types.full_block import FullBlock
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.ints import uint16
|
from src.util.ints import uint16
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
|
|
||||||
|
|
||||||
class RpcClient:
|
class RpcClient:
|
||||||
|
|
|
@ -5,7 +5,7 @@ from typing import Any, Callable, List, Optional
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
|
||||||
from src.full_node import FullNode
|
from src.full_node.full_node import FullNode
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.peer_info import PeerInfo
|
from src.types.peer_info import PeerInfo
|
||||||
|
|
|
@ -11,17 +11,17 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
uvloop = None
|
uvloop = None
|
||||||
|
|
||||||
from src.blockchain import Blockchain
|
from src.full_node.blockchain import Blockchain
|
||||||
from src.consensus.constants import constants
|
from src.consensus.constants import constants
|
||||||
from src.store import FullNodeStore
|
from src.full_node.store import FullNodeStore
|
||||||
from src.full_node import FullNode
|
from src.full_node.full_node import FullNode
|
||||||
from src.rpc.rpc_server import start_rpc_server
|
from src.rpc.rpc_server import start_rpc_server
|
||||||
from src.mempool_manager import MempoolManager
|
from src.full_node.mempool_manager import MempoolManager
|
||||||
from src.server.server import ChiaServer
|
from src.server.server import ChiaServer
|
||||||
from src.server.connection import NodeType
|
from src.server.connection import NodeType
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.peer_info import PeerInfo
|
from src.types.peer_info import PeerInfo
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from src.util.logging import initialize_logging
|
from src.util.logging import initialize_logging
|
||||||
from src.util.config import load_config_cli
|
from src.util.config import load_config_cli
|
||||||
from setproctitle import setproctitle
|
from setproctitle import setproctitle
|
||||||
|
|
|
@ -2,7 +2,7 @@ import asyncio
|
||||||
import signal
|
import signal
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from src.wallet.wallet import Wallet
|
from src.wallet.wallet_node import WalletNode
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import uvloop
|
import uvloop
|
||||||
|
@ -29,7 +29,7 @@ async def main():
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
setproctitle("Chia_Wallet")
|
setproctitle("Chia_Wallet")
|
||||||
|
|
||||||
wallet = await Wallet.create(config, key_config)
|
wallet = await WalletNode.create(config, key_config)
|
||||||
|
|
||||||
full_node_peer = PeerInfo(
|
full_node_peer = PeerInfo(
|
||||||
config["full_node_peer"]["host"], config["full_node_peer"]["port"]
|
config["full_node_peer"]["host"], config["full_node_peer"]["port"]
|
||||||
|
|
|
@ -19,8 +19,11 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Timelord:
|
class Timelord:
|
||||||
def __init__(self, config: Dict):
|
def __init__(
|
||||||
|
self, config: Dict, discrimant_size_bits=constants["DISCRIMINANT_SIZE_BITS"]
|
||||||
|
):
|
||||||
self.config: Dict = config
|
self.config: Dict = config
|
||||||
|
self.discriminant_size_bits = discrimant_size_bits
|
||||||
self.free_servers: List[Tuple[str, str]] = list(
|
self.free_servers: List[Tuple[str, str]] = list(
|
||||||
zip(self.config["vdf_server_ips"], self.config["vdf_server_ports"])
|
zip(self.config["vdf_server_ips"], self.config["vdf_server_ports"])
|
||||||
)
|
)
|
||||||
|
@ -202,9 +205,7 @@ class Timelord:
|
||||||
async def _do_process_communication(
|
async def _do_process_communication(
|
||||||
self, challenge_hash, challenge_weight, ip, port
|
self, challenge_hash, challenge_weight, ip, port
|
||||||
):
|
):
|
||||||
disc: int = create_discriminant(
|
disc: int = create_discriminant(challenge_hash, self.discriminant_size_bits)
|
||||||
challenge_hash, constants["DISCRIMINANT_SIZE_BITS"]
|
|
||||||
)
|
|
||||||
|
|
||||||
log.info("Attempting SSH connection")
|
log.info("Attempting SSH connection")
|
||||||
proc = await asyncio.create_subprocess_shell(
|
proc = await asyncio.create_subprocess_shell(
|
||||||
|
@ -310,7 +311,7 @@ class Timelord:
|
||||||
self.config["n_wesolowski"],
|
self.config["n_wesolowski"],
|
||||||
proof_bytes,
|
proof_bytes,
|
||||||
)
|
)
|
||||||
if not proof_of_time.is_valid(constants["DISCRIMINANT_SIZE_BITS"]):
|
if not proof_of_time.is_valid(self.discriminant_size_bits):
|
||||||
log.error("Invalid proof of time")
|
log.error("Invalid proof of time")
|
||||||
|
|
||||||
response = timelord_protocol.ProofOfTimeFinished(proof_of_time)
|
response = timelord_protocol.ProofOfTimeFinished(proof_of_time)
|
||||||
|
|
|
@ -2,8 +2,8 @@ from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from src.types.hashable.BLSSignature import BLSSignature
|
from src.types.hashable.BLSSignature import BLSSignature
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64
|
||||||
from src.util.streamable import Streamable, streamable
|
from src.util.streamable import Streamable, streamable
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import Tuple, List, Optional
|
||||||
|
|
||||||
from src.types.name_puzzle_condition import NPC
|
from src.types.name_puzzle_condition import NPC
|
||||||
from src.types.body import Body
|
from src.types.body import Body
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.mempool_check_conditions import get_name_puzzle_conditions
|
from src.util.mempool_check_conditions import get_name_puzzle_conditions
|
||||||
|
@ -50,6 +50,21 @@ class FullBlock(Streamable):
|
||||||
def header_hash(self) -> bytes32:
|
def header_hash(self) -> bytes32:
|
||||||
return self.header.header_hash
|
return self.header.header_hash
|
||||||
|
|
||||||
|
def additions(self) -> List[Coin]:
|
||||||
|
additions: List[Coin] = []
|
||||||
|
|
||||||
|
if self.body.transactions is not None:
|
||||||
|
# This should never throw here, block must be valid if it comes to here
|
||||||
|
err, npc_list, cost = get_name_puzzle_conditions(self.body.transactions)
|
||||||
|
# created coins
|
||||||
|
if npc_list is not None:
|
||||||
|
additions.extend(additions_for_npc(npc_list))
|
||||||
|
|
||||||
|
additions.append(self.body.coinbase)
|
||||||
|
additions.append(self.body.fees_coin)
|
||||||
|
|
||||||
|
return additions
|
||||||
|
|
||||||
async def tx_removals_and_additions(self) -> Tuple[List[bytes32], List[Coin]]:
|
async def tx_removals_and_additions(self) -> Tuple[List[bytes32], List[Coin]]:
|
||||||
"""
|
"""
|
||||||
Doesn't return coinbase and fee reward.
|
Doesn't return coinbase and fee reward.
|
||||||
|
@ -60,11 +75,8 @@ class FullBlock(Streamable):
|
||||||
additions: List[Coin] = []
|
additions: List[Coin] = []
|
||||||
|
|
||||||
if self.body.transactions is not None:
|
if self.body.transactions is not None:
|
||||||
# ensure block program generates solutions
|
|
||||||
# This should never throw here, block must be valid if it comes to here
|
# This should never throw here, block must be valid if it comes to here
|
||||||
err, npc_list, cost = await get_name_puzzle_conditions(
|
err, npc_list, cost = get_name_puzzle_conditions(self.body.transactions)
|
||||||
self.body.transactions
|
|
||||||
)
|
|
||||||
# build removals list
|
# build removals list
|
||||||
if npc_list is None:
|
if npc_list is None:
|
||||||
return [], []
|
return [], []
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import io
|
import io
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from clvm.casts import int_to_bytes, int_from_bytes
|
from clvm.casts import int_to_bytes, int_from_bytes
|
||||||
|
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
|
from src.util.hash import std_hash
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64
|
||||||
from src.util.streamable import streamable, Streamable
|
from src.util.streamable import streamable, Streamable
|
||||||
|
|
||||||
|
@ -22,6 +24,10 @@ class Coin(Streamable):
|
||||||
def name(self) -> bytes32:
|
def name(self) -> bytes32:
|
||||||
return self.get_hash()
|
return self.get_hash()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name_str(self) -> str:
|
||||||
|
return self.name().hex()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bytes(cls, blob):
|
def from_bytes(cls, blob):
|
||||||
parent_coin_info = blob[:32]
|
parent_coin_info = blob[:32]
|
||||||
|
@ -35,3 +41,13 @@ class Coin(Streamable):
|
||||||
f.write(self.puzzle_hash)
|
f.write(self.puzzle_hash)
|
||||||
f.write(int_to_bytes(self.amount))
|
f.write(int_to_bytes(self.amount))
|
||||||
return f.getvalue()
|
return f.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def hash_coin_list(coin_list: List[Coin]) -> bytes32:
|
||||||
|
coin_list.sort(key=lambda x: x.name_str, reverse=True)
|
||||||
|
buffer = bytearray()
|
||||||
|
|
||||||
|
for coin in coin_list:
|
||||||
|
buffer.extend(coin.name())
|
||||||
|
|
||||||
|
return std_hash(buffer)
|
||||||
|
|
|
@ -44,5 +44,4 @@ class Program(SExp): # type: ignore # noqa
|
||||||
return bytes(self).hex()
|
return bytes(self).hex()
|
||||||
|
|
||||||
def get_hash(self) -> bytes32:
|
def get_hash(self) -> bytes32:
|
||||||
# print("Bytes self", bytes(self))
|
|
||||||
return bytes32(std_hash(bytes(self)))
|
return bytes32(std_hash(bytes(self)))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.streamable import Streamable, streamable
|
from src.util.streamable import Streamable, streamable
|
||||||
from src.util.ints import uint32
|
from src.util.ints import uint32
|
|
@ -1,7 +1,7 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from .Coin import Coin
|
from .coin import Coin
|
||||||
from .Program import Program
|
from .program import Program
|
||||||
from src.util.streamable import Streamable, streamable
|
from src.util.streamable import Streamable, streamable
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
|
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.chain_utils import additions_for_solution
|
from src.util.chain_utils import additions_for_solution
|
||||||
from src.util.streamable import Streamable, streamable
|
from src.util.streamable import Streamable, streamable
|
||||||
from .BLSSignature import BLSSignature
|
from .BLSSignature import BLSSignature
|
||||||
from .CoinSolution import CoinSolution
|
from .coin_solution import CoinSolution
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
|
@ -13,7 +13,7 @@ class HeaderData(Streamable):
|
||||||
height: uint32
|
height: uint32
|
||||||
prev_header_hash: bytes32
|
prev_header_hash: bytes32
|
||||||
timestamp: uint64
|
timestamp: uint64
|
||||||
filter_hash: bytes32
|
filter: bytes
|
||||||
proof_of_space_hash: bytes32
|
proof_of_space_hash: bytes32
|
||||||
body_hash: bytes32
|
body_hash: bytes32
|
||||||
weight: uint64
|
weight: uint64
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.condition_tools import ConditionOpcode
|
from src.util.condition_tools import ConditionOpcode
|
||||||
|
|
||||||
|
|
|
@ -331,7 +331,7 @@ class FullNodeUI:
|
||||||
else:
|
else:
|
||||||
self.syncing.text = f"Syncing"
|
self.syncing.text = f"Syncing"
|
||||||
else:
|
else:
|
||||||
self.syncing.text = "Not syncing"
|
self.syncing.text = "Synced"
|
||||||
|
|
||||||
total_iters = self.lca_block.data.total_iters
|
total_iters = self.lca_block.data.total_iters
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ from typing import Optional, Dict, List
|
||||||
|
|
||||||
from clvm.casts import int_from_bytes
|
from clvm.casts import int_from_bytes
|
||||||
|
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
from src.types.header import Header
|
from src.types.header import Header
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.condition_tools import ConditionOpcode
|
from src.util.condition_tools import ConditionOpcode
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
|
|
||||||
|
|
||||||
def best_solution_program(bundle: SpendBundle) -> Program:
|
def best_solution_program(bundle: SpendBundle) -> Program:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.util.condition_tools import (
|
from src.util.condition_tools import (
|
||||||
created_outputs_for_conditions_dict,
|
created_outputs_for_conditions_dict,
|
||||||
conditions_dict_for_solution,
|
conditions_dict_for_solution,
|
||||||
|
|
|
@ -6,11 +6,11 @@ from clvm.EvalError import EvalError
|
||||||
from clvm.casts import int_from_bytes
|
from clvm.casts import int_from_bytes
|
||||||
from clvm.subclass_sexp import BaseSExp
|
from clvm.subclass_sexp import BaseSExp
|
||||||
|
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from src.types.hashable.BLSSignature import BLSSignature, BLSPublicKey
|
from src.types.hashable.BLSSignature import BLSSignature, BLSPublicKey
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from .ConsensusError import Err, ConsensusError
|
from .ConsensusError import Err, ConsensusError
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,12 @@ import clvm
|
||||||
from clvm import EvalError
|
from clvm import EvalError
|
||||||
from clvm.casts import int_from_bytes
|
from clvm.casts import int_from_bytes
|
||||||
|
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
from src.types.name_puzzle_condition import NPC
|
from src.types.name_puzzle_condition import NPC
|
||||||
from src.mempool import Mempool
|
from src.full_node.mempool import Mempool
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.condition_tools import ConditionOpcode, conditions_dict_for_solution
|
from src.util.condition_tools import ConditionOpcode, conditions_dict_for_solution
|
||||||
from src.util.ConsensusError import Err
|
from src.util.ConsensusError import Err
|
||||||
|
@ -91,7 +91,7 @@ def mempool_assert_time_exceeds(condition: ConditionVarPair):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def get_name_puzzle_conditions(
|
def get_name_puzzle_conditions(
|
||||||
block_program: Program,
|
block_program: Program,
|
||||||
) -> Tuple[Optional[Err], List[NPC], int]:
|
) -> Tuple[Optional[Err], List[NPC], int]:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,357 @@
|
||||||
|
from hashlib import blake2b, sha256
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
"""
|
||||||
|
A simple, confidence-inspiring Merkle Set standard
|
||||||
|
|
||||||
|
Advantages of this standard:
|
||||||
|
Low CPU requirements
|
||||||
|
Small proofs of inclusion/exclusion
|
||||||
|
Reasonably simple implementation
|
||||||
|
|
||||||
|
The main tricks in this standard are:
|
||||||
|
|
||||||
|
Uses blake2b because that has the best performance on 512 bit inputs
|
||||||
|
Skips repeated hashing of exactly two things even when they share prefix bits
|
||||||
|
|
||||||
|
|
||||||
|
Proofs support proving including/exclusion for a large number of values in
|
||||||
|
a single string. They're a serialization of a subset of the tree.
|
||||||
|
|
||||||
|
Proof format:
|
||||||
|
|
||||||
|
multiproof: subtree
|
||||||
|
subtree: middle or terminal or truncated or empty
|
||||||
|
middle: MIDDLE 1 subtree subtree
|
||||||
|
terminal: TERMINAL 1 hash 32
|
||||||
|
# If the sibling is empty truncated implies more than two children.
|
||||||
|
truncated: TRUNCATED 1 hash 32
|
||||||
|
empty: EMPTY 1
|
||||||
|
EMPTY: \x00
|
||||||
|
TERMINAL: \x01
|
||||||
|
MIDDLE: \x02
|
||||||
|
TRUNCATED: \x03
|
||||||
|
"""
|
||||||
|
|
||||||
|
EMPTY = bytes([0])
|
||||||
|
TERMINAL = bytes([1])
|
||||||
|
MIDDLE = bytes([2])
|
||||||
|
TRUNCATED = bytes([3])
|
||||||
|
|
||||||
|
BLANK = bytes([0] * 32)
|
||||||
|
|
||||||
|
prehashed: Dict = {}
|
||||||
|
|
||||||
|
|
||||||
|
def init_prehashed():
|
||||||
|
for x in [EMPTY, TERMINAL, MIDDLE]:
|
||||||
|
for y in [EMPTY, TERMINAL, MIDDLE]:
|
||||||
|
prehashed[x + y] = blake2b(bytes([0] * 30) + x + y)
|
||||||
|
|
||||||
|
|
||||||
|
init_prehashed()
|
||||||
|
|
||||||
|
|
||||||
|
def hashdown(mystr):
|
||||||
|
assert len(mystr) == 66
|
||||||
|
h = prehashed[bytes(mystr[0:1] + mystr[33:34])].copy()
|
||||||
|
h.update(mystr[1:33] + mystr[34:])
|
||||||
|
return h.digest()[:32]
|
||||||
|
|
||||||
|
|
||||||
|
def compress_root(mystr):
|
||||||
|
assert len(mystr) == 33
|
||||||
|
if mystr[0:1] == MIDDLE:
|
||||||
|
return mystr[1:]
|
||||||
|
if mystr[0:1] == EMPTY:
|
||||||
|
assert mystr[1:] == BLANK
|
||||||
|
return BLANK
|
||||||
|
return blake2b(mystr).digest()[:32]
|
||||||
|
|
||||||
|
|
||||||
|
def get_bit(mybytes, pos):
|
||||||
|
assert len(mybytes) == 32
|
||||||
|
return (mybytes[pos // 8] >> (7 - (pos % 8))) & 1
|
||||||
|
|
||||||
|
|
||||||
|
class MerkleSet:
|
||||||
|
def __init__(self, root=None):
|
||||||
|
self.root = root
|
||||||
|
if root is None:
|
||||||
|
self.root = _empty
|
||||||
|
|
||||||
|
def get_root(self):
|
||||||
|
return compress_root(self.root.get_hash())
|
||||||
|
|
||||||
|
def add_already_hashed(self, toadd):
|
||||||
|
self.root = self.root.add(toadd, 0)
|
||||||
|
|
||||||
|
def remove_already_hashed(self, toremove):
|
||||||
|
self.root = self.root.remove(toremove, 0)
|
||||||
|
|
||||||
|
def is_included_already_hashed(self, tocheck):
|
||||||
|
proof: List = []
|
||||||
|
r = self.root.is_included(tocheck, 0, proof)
|
||||||
|
return r, b"".join(proof)
|
||||||
|
|
||||||
|
def _audit(self, hashes):
|
||||||
|
newhashes: List = []
|
||||||
|
self.root._audit(newhashes, [])
|
||||||
|
assert newhashes == sorted(newhashes)
|
||||||
|
|
||||||
|
|
||||||
|
class EmptyNode:
|
||||||
|
def __init__(self):
|
||||||
|
self.hash = BLANK
|
||||||
|
|
||||||
|
def get_hash(self):
|
||||||
|
return EMPTY + BLANK
|
||||||
|
|
||||||
|
def is_empty(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_terminal(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_double(self):
|
||||||
|
raise SetError()
|
||||||
|
|
||||||
|
def add(self, toadd, depth):
|
||||||
|
return TerminalNode(toadd)
|
||||||
|
|
||||||
|
def remove(self, toremove, depth):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def is_included(self, tocheck, depth, p):
|
||||||
|
p.append(EMPTY)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def other_included(self, tocheck, depth, p, collapse):
|
||||||
|
p.append(EMPTY)
|
||||||
|
|
||||||
|
def _audit(self, hashes, bits):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
_empty = EmptyNode()
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalNode:
|
||||||
|
def __init__(self, hash, bits=None):
|
||||||
|
assert len(hash) == 32
|
||||||
|
self.hash = hash
|
||||||
|
if bits is not None:
|
||||||
|
self._audit([], bits)
|
||||||
|
|
||||||
|
def get_hash(self):
|
||||||
|
return TERMINAL + self.hash
|
||||||
|
|
||||||
|
def is_empty(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_terminal(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_double(self):
|
||||||
|
raise SetError()
|
||||||
|
|
||||||
|
def add(self, toadd, depth):
|
||||||
|
if toadd == self.hash:
|
||||||
|
return self
|
||||||
|
if toadd > self.hash:
|
||||||
|
return self._make_middle([self, TerminalNode(toadd)], depth)
|
||||||
|
else:
|
||||||
|
return self._make_middle([TerminalNode(toadd), self], depth)
|
||||||
|
|
||||||
|
def _make_middle(self, children, depth):
|
||||||
|
cbits = [get_bit(child.hash, depth) for child in children]
|
||||||
|
if cbits[0] != cbits[1]:
|
||||||
|
return MiddleNode(children)
|
||||||
|
nextvals = [None, None]
|
||||||
|
nextvals[cbits[0] ^ 1] = _empty # type: ignore
|
||||||
|
nextvals[cbits[0]] = self._make_middle(children, depth + 1)
|
||||||
|
return MiddleNode(nextvals)
|
||||||
|
|
||||||
|
def remove(self, toremove, depth):
|
||||||
|
if toremove == self.hash:
|
||||||
|
return _empty
|
||||||
|
return self
|
||||||
|
|
||||||
|
def is_included(self, tocheck, depth, proof):
|
||||||
|
proof.append(TERMINAL + self.hash)
|
||||||
|
return tocheck == self.hash
|
||||||
|
|
||||||
|
def other_included(self, tocheck, depth, p, collapse):
|
||||||
|
p.append(TERMINAL + self.hash)
|
||||||
|
|
||||||
|
def _audit(self, hashes, bits):
|
||||||
|
hashes.append(self.hash)
|
||||||
|
for pos, v in enumerate(bits):
|
||||||
|
assert get_bit(self.hash, pos) == v
|
||||||
|
|
||||||
|
|
||||||
|
class MiddleNode:
|
||||||
|
def __init__(self, children):
|
||||||
|
self.children = children
|
||||||
|
if children[0].is_empty() and children[1].is_double():
|
||||||
|
self.hash = children[1].hash
|
||||||
|
elif children[1].is_empty() and children[0].is_double():
|
||||||
|
self.hash = children[0].hash
|
||||||
|
else:
|
||||||
|
if children[0].is_empty() and (
|
||||||
|
children[1].is_empty() or children[1].is_terminal()
|
||||||
|
):
|
||||||
|
raise SetError()
|
||||||
|
if children[1].is_empty() and children[0].is_terminal():
|
||||||
|
raise SetError
|
||||||
|
if (
|
||||||
|
children[0].is_terminal()
|
||||||
|
and children[1].is_terminal()
|
||||||
|
and children[0].hash >= children[1].hash
|
||||||
|
):
|
||||||
|
raise SetError
|
||||||
|
self.hash = hashdown(children[0].get_hash() + children[1].get_hash())
|
||||||
|
|
||||||
|
def get_hash(self):
|
||||||
|
return MIDDLE + self.hash
|
||||||
|
|
||||||
|
def is_empty(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_terminal(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_double(self):
|
||||||
|
if self.children[0].is_empty():
|
||||||
|
return self.children[1].is_double()
|
||||||
|
if self.children[1].is_empty():
|
||||||
|
return self.children[0].is_double()
|
||||||
|
return self.children[0].is_terminal() and self.children[1].is_terminal()
|
||||||
|
|
||||||
|
def add(self, toadd, depth):
|
||||||
|
bit = get_bit(toadd, depth)
|
||||||
|
child = self.children[bit]
|
||||||
|
newchild = child.add(toadd, depth + 1)
|
||||||
|
if newchild is child:
|
||||||
|
return self
|
||||||
|
newvals = [x for x in self.children]
|
||||||
|
newvals[bit] = newchild
|
||||||
|
return MiddleNode(newvals)
|
||||||
|
|
||||||
|
def remove(self, toremove, depth):
|
||||||
|
bit = get_bit(toremove, depth)
|
||||||
|
child = self.children[bit]
|
||||||
|
newchild = child.remove(toremove, depth + 1)
|
||||||
|
if newchild is child:
|
||||||
|
return self
|
||||||
|
otherchild = self.children[bit ^ 1]
|
||||||
|
if newchild.is_empty() and otherchild.is_terminal():
|
||||||
|
return otherchild
|
||||||
|
if newchild.is_terminal() and otherchild.is_empty():
|
||||||
|
return newchild
|
||||||
|
newvals = [x for x in self.children]
|
||||||
|
newvals[bit] = newchild
|
||||||
|
return MiddleNode(newvals)
|
||||||
|
|
||||||
|
def is_included(self, tocheck, depth, p):
|
||||||
|
p.append(MIDDLE)
|
||||||
|
if get_bit(tocheck, depth) == 0:
|
||||||
|
r = self.children[0].is_included(tocheck, depth + 1, p)
|
||||||
|
self.children[1].other_included(
|
||||||
|
tocheck, depth + 1, p, not self.children[0].is_empty()
|
||||||
|
)
|
||||||
|
return r
|
||||||
|
else:
|
||||||
|
self.children[0].other_included(
|
||||||
|
tocheck, depth + 1, p, not self.children[1].is_empty()
|
||||||
|
)
|
||||||
|
return self.children[1].is_included(tocheck, depth + 1, p)
|
||||||
|
|
||||||
|
def other_included(self, tocheck, depth, p, collapse):
|
||||||
|
if collapse or not self.is_double():
|
||||||
|
p.append(TRUNCATED + self.hash)
|
||||||
|
else:
|
||||||
|
self.is_included(tocheck, depth, p)
|
||||||
|
|
||||||
|
def _audit(self, hashes, bits):
|
||||||
|
self.children[0]._audit(hashes, bits + [0])
|
||||||
|
self.children[1]._audit(hashes, bits + [1])
|
||||||
|
|
||||||
|
|
||||||
|
class TruncatedNode:
|
||||||
|
def __init__(self, hash):
|
||||||
|
self.hash = hash
|
||||||
|
|
||||||
|
def get_hash(self):
|
||||||
|
return MIDDLE + self.hash
|
||||||
|
|
||||||
|
def is_empty(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_terminal(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_double(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_included(self, tocheck, depth, p):
|
||||||
|
raise SetError()
|
||||||
|
|
||||||
|
def other_included(self, tocheck, depth, p, collapse):
|
||||||
|
p.append(TRUNCATED + self.hash)
|
||||||
|
|
||||||
|
|
||||||
|
class SetError(BaseException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_included(root, val, proof):
|
||||||
|
return confirm_not_included_already_hashed(root, sha256(val).digest(), proof)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_included_already_hashed(root, val, proof):
|
||||||
|
return _confirm(root, val, proof, True)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_not_included(root, val, proof):
|
||||||
|
return confirm_not_included_already_hashed(root, sha256(val).digest(), proof)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_not_included_already_hashed(root, val, proof):
|
||||||
|
return _confirm(root, val, proof, False)
|
||||||
|
|
||||||
|
|
||||||
|
def _confirm(root, val, proof, expected):
|
||||||
|
try:
|
||||||
|
p = deserialize_proof(proof)
|
||||||
|
if p.get_root() != root:
|
||||||
|
return False
|
||||||
|
r, junk = p.is_included_already_hashed(val)
|
||||||
|
return r == expected
|
||||||
|
except SetError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def deserialize_proof(proof):
|
||||||
|
try:
|
||||||
|
r, pos = _deserialize(proof, 0, [])
|
||||||
|
if pos != len(proof):
|
||||||
|
raise SetError()
|
||||||
|
return MerkleSet(r)
|
||||||
|
except IndexError:
|
||||||
|
raise SetError()
|
||||||
|
|
||||||
|
|
||||||
|
def _deserialize(proof, pos, bits):
|
||||||
|
t = proof[pos : pos + 1] # flake8: noqa
|
||||||
|
if t == EMPTY:
|
||||||
|
return _empty, pos + 1
|
||||||
|
if t == TERMINAL:
|
||||||
|
return TerminalNode(proof[pos + 1 : pos + 33], bits), pos + 33
|
||||||
|
if t == TRUNCATED:
|
||||||
|
return TruncatedNode(proof[pos + 1 : pos + 33]), pos + 33
|
||||||
|
if t != MIDDLE:
|
||||||
|
raise SetError()
|
||||||
|
v0, pos = _deserialize(proof, pos + 1, bits + [0])
|
||||||
|
v1, pos = _deserialize(proof, pos, bits + [1])
|
||||||
|
return MiddleNode([v0, v1]), pos
|
|
@ -7,7 +7,7 @@ import pprint
|
||||||
import json
|
import json
|
||||||
from typing import Any, BinaryIO, List, Type, get_type_hints, Union
|
from typing import Any, BinaryIO, List, Type, get_type_hints, Union
|
||||||
from src.util.byte_types import hexstr_to_bytes
|
from src.util.byte_types import hexstr_to_bytes
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.util.hash import std_hash
|
from src.util.hash import std_hash
|
||||||
|
|
||||||
from blspy import (
|
from blspy import (
|
||||||
|
@ -181,7 +181,7 @@ class Streamable:
|
||||||
f.write(uint32(len(item)).to_bytes(4, "big"))
|
f.write(uint32(len(item)).to_bytes(4, "big"))
|
||||||
f.write(item.encode("utf-8"))
|
f.write(item.encode("utf-8"))
|
||||||
elif f_type is bool:
|
elif f_type is bool:
|
||||||
f.write(bytes(item))
|
f.write(int(item).to_bytes(4, "big"))
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"can't stream {item}, {f_type}")
|
raise NotImplementedError(f"can't stream {item}, {f_type}")
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
# Install
|
# Electron Wallet
|
||||||
python3 -m venv .venv
|
## Install
|
||||||
. .venv/bin/activate
|
|
||||||
|
|
||||||
pip install zerorpc
|
```npm install --runtime=electron --target=1.7.6```
|
||||||
pip install pyinstaller
|
|
||||||
|
|
||||||
npm install --runtime=electron --target=1.7.6
|
## Run:
|
||||||
npm install electron-rebuild && ./node_modules/.bin/electron-rebuild
|
```npm start```
|
||||||
|
|
||||||
|
## Error
|
||||||
|
If run fails because of electron try doing this
|
||||||
|
|
||||||
|
```npm install electron-rebuild && ./node_modules/.bin/electron-rebuild```
|
||||||
|
|
|
@ -9,7 +9,7 @@ const path = require('path')
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
|
||||||
const PY_DIST_FOLDER = 'pydist'
|
const PY_DIST_FOLDER = 'pydist'
|
||||||
const PY_FOLDER = 'wallet_rpc'
|
const PY_FOLDER = 'rpc'
|
||||||
const PY_MODULE = 'rpc_wallet' // without .py suffix
|
const PY_MODULE = 'rpc_wallet' // without .py suffix
|
||||||
|
|
||||||
let pyProc = null
|
let pyProc = null
|
||||||
|
|
|
@ -177,15 +177,6 @@
|
||||||
"chainsaw": "~0.1.0"
|
"chainsaw": "~0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bl": {
|
|
||||||
"version": "1.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
|
||||||
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
|
|
||||||
"requires": {
|
|
||||||
"readable-stream": "^2.3.5",
|
|
||||||
"safe-buffer": "^5.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.7.2",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||||
|
@ -504,14 +495,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||||
},
|
},
|
||||||
"decompress-response": {
|
|
||||||
"version": "3.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
|
|
||||||
"integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
|
|
||||||
"requires": {
|
|
||||||
"mimic-response": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"decompress-zip": {
|
"decompress-zip": {
|
||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.3.2.tgz",
|
||||||
|
@ -556,7 +539,8 @@
|
||||||
"deep-extend": {
|
"deep-extend": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"deep-is": {
|
"deep-is": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
|
@ -858,14 +842,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
||||||
},
|
},
|
||||||
"end-of-stream": {
|
|
||||||
"version": "1.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
|
||||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
|
||||||
"requires": {
|
|
||||||
"once": "^1.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"env-paths": {
|
"env-paths": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz",
|
||||||
|
@ -998,16 +974,6 @@
|
||||||
"es5-ext": "~0.10.14"
|
"es5-ext": "~0.10.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event-lite": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/event-lite/-/event-lite-0.1.2.tgz",
|
|
||||||
"integrity": "sha512-HnSYx1BsJ87/p6swwzv+2v6B4X+uxUteoDfRxsAb1S1BePzQqOLevVmkdA15GHJVd9A9Ok6wygUR18Hu0YeV9g=="
|
|
||||||
},
|
|
||||||
"expand-template": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-cebqLtV8KOZfw0UI8TEFWxtczxxC1jvyUvx6H4fyp1K1FN7A4Q+uggVUlOsI1K8AGU0rwOGqP8nCapdrw8CYQg=="
|
|
||||||
},
|
|
||||||
"ext": {
|
"ext": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
||||||
|
@ -1094,11 +1060,6 @@
|
||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fs-constants": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
|
||||||
},
|
|
||||||
"fs-extra": {
|
"fs-extra": {
|
||||||
"version": "0.30.0",
|
"version": "0.30.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
|
||||||
|
@ -1240,11 +1201,6 @@
|
||||||
"assert-plus": "^1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"github-from-package": {
|
|
||||||
"version": "0.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
|
||||||
"integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
|
|
||||||
},
|
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.6",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
|
@ -1356,18 +1312,14 @@
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"insert-css": {
|
"insert-css": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz",
|
||||||
"integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ="
|
"integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ="
|
||||||
},
|
},
|
||||||
"int64-buffer": {
|
|
||||||
"version": "0.1.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.10.tgz",
|
|
||||||
"integrity": "sha1-J3siiofZWtd30HwTgyAiQGpHNCM="
|
|
||||||
},
|
|
||||||
"is-arrayish": {
|
"is-arrayish": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||||
|
@ -1611,11 +1563,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
|
||||||
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
|
||||||
},
|
},
|
||||||
"mimic-response": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
|
|
||||||
},
|
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
@ -1698,22 +1645,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
"msgpack-lite": {
|
|
||||||
"version": "0.1.26",
|
|
||||||
"resolved": "https://registry.npmjs.org/msgpack-lite/-/msgpack-lite-0.1.26.tgz",
|
|
||||||
"integrity": "sha1-3TxQsm8FnyXn7e42REGDWOKprYk=",
|
|
||||||
"requires": {
|
|
||||||
"event-lite": "^0.1.1",
|
|
||||||
"ieee754": "^1.1.8",
|
|
||||||
"int64-buffer": "^0.1.9",
|
|
||||||
"isarray": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nan": {
|
|
||||||
"version": "2.14.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
|
|
||||||
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
|
|
||||||
},
|
|
||||||
"next-tick": {
|
"next-tick": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
|
||||||
|
@ -1761,11 +1692,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"noop-logger": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
|
||||||
"integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
|
|
||||||
},
|
|
||||||
"nopt": {
|
"nopt": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
|
||||||
|
@ -2049,28 +1975,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
||||||
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
|
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
|
||||||
},
|
},
|
||||||
"prebuild-install": {
|
|
||||||
"version": "2.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-2.5.3.tgz",
|
|
||||||
"integrity": "sha512-/rI36cN2g7vDQnKWN8Uzupi++KjyqS9iS+/fpwG4Ea8d0Pip0PQ5bshUNzVwt+/D2MRfhVAplYMMvWLqWrCF/g==",
|
|
||||||
"requires": {
|
|
||||||
"detect-libc": "^1.0.3",
|
|
||||||
"expand-template": "^1.0.2",
|
|
||||||
"github-from-package": "0.0.0",
|
|
||||||
"minimist": "^1.2.0",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"node-abi": "^2.2.0",
|
|
||||||
"noop-logger": "^0.1.1",
|
|
||||||
"npmlog": "^4.0.1",
|
|
||||||
"os-homedir": "^1.0.1",
|
|
||||||
"pump": "^2.0.1",
|
|
||||||
"rc": "^1.1.6",
|
|
||||||
"simple-get": "^2.7.0",
|
|
||||||
"tar-fs": "^1.13.0",
|
|
||||||
"tunnel-agent": "^0.6.0",
|
|
||||||
"which-pm-runs": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||||
|
@ -2137,15 +2041,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
|
||||||
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
|
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
|
||||||
},
|
},
|
||||||
"pump": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
|
|
||||||
"requires": {
|
|
||||||
"end-of-stream": "^1.1.0",
|
|
||||||
"once": "^1.3.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
@ -2310,6 +2205,7 @@
|
||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||||
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"deep-extend": "^0.6.0",
|
"deep-extend": "^0.6.0",
|
||||||
"ini": "~1.3.0",
|
"ini": "~1.3.0",
|
||||||
|
@ -2505,21 +2401,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||||
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
|
||||||
},
|
},
|
||||||
"simple-concat": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
|
|
||||||
},
|
|
||||||
"simple-get": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
|
||||||
"requires": {
|
|
||||||
"decompress-response": "^3.3.0",
|
|
||||||
"once": "^1.3.1",
|
|
||||||
"simple-concat": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"single-line-log": {
|
"single-line-log": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz",
|
||||||
|
@ -2705,7 +2586,8 @@
|
||||||
"strip-json-comments": {
|
"strip-json-comments": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"sumchecker": {
|
"sumchecker": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
|
@ -2739,42 +2621,6 @@
|
||||||
"yallist": "^3.0.3"
|
"yallist": "^3.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tar-fs": {
|
|
||||||
"version": "1.16.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-1.16.3.tgz",
|
|
||||||
"integrity": "sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw==",
|
|
||||||
"requires": {
|
|
||||||
"chownr": "^1.0.1",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"pump": "^1.0.0",
|
|
||||||
"tar-stream": "^1.1.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"pump": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw==",
|
|
||||||
"requires": {
|
|
||||||
"end-of-stream": "^1.1.0",
|
|
||||||
"once": "^1.3.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tar-stream": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
|
|
||||||
"integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
|
|
||||||
"requires": {
|
|
||||||
"bl": "^1.0.0",
|
|
||||||
"buffer-alloc": "^1.2.0",
|
|
||||||
"end-of-stream": "^1.0.0",
|
|
||||||
"fs-constants": "^1.0.0",
|
|
||||||
"readable-stream": "^2.3.0",
|
|
||||||
"to-buffer": "^1.1.1",
|
|
||||||
"xtend": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"throttleit": {
|
"throttleit": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz",
|
||||||
|
@ -2835,11 +2681,6 @@
|
||||||
"os-tmpdir": "~1.0.1"
|
"os-tmpdir": "~1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"to-buffer": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
|
|
||||||
},
|
|
||||||
"touch": {
|
"touch": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz",
|
||||||
|
@ -2926,11 +2767,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
},
|
},
|
||||||
"underscore": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.3.3.tgz",
|
|
||||||
"integrity": "sha1-R6xTaD2vgyv6lS4XdEF9pHgXrkI="
|
|
||||||
},
|
|
||||||
"universalify": {
|
"universalify": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||||
|
@ -3006,11 +2842,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
|
||||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
|
||||||
},
|
},
|
||||||
"which-pm-runs": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
|
|
||||||
},
|
|
||||||
"wide-align": {
|
"wide-align": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
|
||||||
|
@ -3211,25 +3042,6 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"fd-slicer": "~1.0.1"
|
"fd-slicer": "~1.0.1"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"zeromq": {
|
|
||||||
"version": "4.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/zeromq/-/zeromq-4.6.0.tgz",
|
|
||||||
"integrity": "sha512-sU7pQqQj7f/C6orJZAXls+NEKaVMZZtnZqpMPTq5d5dP78CmdC0g15XIviFAN6poPuKl9qlGt74vipOUUuNeWg==",
|
|
||||||
"requires": {
|
|
||||||
"nan": "^2.6.2",
|
|
||||||
"prebuild-install": "^2.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zerorpc": {
|
|
||||||
"version": "git+https://github.com/0rpc/zerorpc-node.git#45d2e510a6dc236863ac9a2b92da0a2511ef54d9",
|
|
||||||
"from": "git+https://github.com/0rpc/zerorpc-node.git",
|
|
||||||
"requires": {
|
|
||||||
"msgpack-lite": "^0.1.26",
|
|
||||||
"underscore": "1.3.3",
|
|
||||||
"uuid": "^3.0.0",
|
|
||||||
"zeromq": "^4.6.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,10 @@
|
||||||
"dialogs": "^2.0.1",
|
"dialogs": "^2.0.1",
|
||||||
"electron-rebuild": "^1.10.0",
|
"electron-rebuild": "^1.10.0",
|
||||||
"jquery": "^3.4.1",
|
"jquery": "^3.4.1",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4"
|
||||||
"zerorpc": "git+https://github.com/0rpc/zerorpc-node.git"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^1.7.6",
|
"electron": "^1.8.8",
|
||||||
"electron-packager": "^9.0.1"
|
"electron-packager": "^9.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,12 @@ import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from blspy import ExtendedPrivateKey
|
|
||||||
from setproctitle import setproctitle
|
|
||||||
|
|
||||||
from src.server.outbound_message import NodeType
|
from src.server.outbound_message import NodeType
|
||||||
from src.server.server import ChiaServer
|
from src.server.server import ChiaServer
|
||||||
from src.types.peer_info import PeerInfo
|
from src.types.peer_info import PeerInfo
|
||||||
from src.util.config import load_config_cli, load_config
|
from src.util.config import load_config
|
||||||
from src.wallet.wallet import Wallet
|
from src.wallet.wallet_node import WalletNode
|
||||||
|
|
||||||
|
|
||||||
class EnhancedJSONEncoder(json.JSONEncoder):
|
class EnhancedJSONEncoder(json.JSONEncoder):
|
||||||
|
@ -44,14 +42,14 @@ class RpcWalletApiHandler:
|
||||||
to the full node.
|
to the full node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, wallet: Wallet):
|
def __init__(self, wallet_node: WalletNode):
|
||||||
self.wallet = wallet
|
self.wallet_node = wallet_node
|
||||||
|
|
||||||
async def get_next_puzzle_hash(self, request) -> web.Response:
|
async def get_next_puzzle_hash(self, request) -> web.Response:
|
||||||
"""
|
"""
|
||||||
Returns a new puzzlehash
|
Returns a new puzzlehash
|
||||||
"""
|
"""
|
||||||
puzzlehash = self.wallet.get_new_puzzlehash().hex()
|
puzzlehash = self.wallet_node.wallet.get_new_puzzlehash().hex()
|
||||||
response = {
|
response = {
|
||||||
"puzzlehash": puzzlehash,
|
"puzzlehash": puzzlehash,
|
||||||
}
|
}
|
||||||
|
@ -62,47 +60,46 @@ class RpcWalletApiHandler:
|
||||||
if "amount" in request_data and "puzzlehash" in request_data:
|
if "amount" in request_data and "puzzlehash" in request_data:
|
||||||
amount = int(request_data["amount"])
|
amount = int(request_data["amount"])
|
||||||
puzzlehash = request_data["puzzlehash"]
|
puzzlehash = request_data["puzzlehash"]
|
||||||
tx = await self.wallet.generate_signed_transaction(amount, puzzlehash)
|
tx = await self.wallet_node.wallet.generate_signed_transaction(
|
||||||
|
amount, puzzlehash
|
||||||
|
)
|
||||||
|
|
||||||
if tx is None:
|
if tx is None:
|
||||||
response = {
|
response = {"success": False}
|
||||||
"success": False
|
|
||||||
}
|
|
||||||
return obj_to_response(response)
|
return obj_to_response(response)
|
||||||
|
|
||||||
await self.wallet.push_transaction(tx)
|
await self.wallet_node.wallet.push_transaction(tx)
|
||||||
|
|
||||||
response = {
|
response = {"success": True}
|
||||||
"success": True
|
|
||||||
}
|
|
||||||
return obj_to_response(response)
|
return obj_to_response(response)
|
||||||
|
|
||||||
response = {
|
response = {"success": False}
|
||||||
"success": False
|
|
||||||
}
|
|
||||||
return obj_to_response(response)
|
return obj_to_response(response)
|
||||||
|
|
||||||
async def get_server_ready(self, request) -> web.Response:
|
async def get_server_ready(self, request) -> web.Response:
|
||||||
|
|
||||||
response = {
|
response = {"success": True}
|
||||||
"success": True
|
|
||||||
}
|
|
||||||
return obj_to_response(response)
|
return obj_to_response(response)
|
||||||
|
|
||||||
async def get_transactions(self, request) -> web.Response:
|
async def get_transactions(self, request) -> web.Response:
|
||||||
|
transactions = (
|
||||||
|
await self.wallet_node.wallet_state_manager.get_all_transactions()
|
||||||
|
)
|
||||||
|
|
||||||
response = {
|
response = {"success": True, "txs": transactions}
|
||||||
"success": True
|
|
||||||
}
|
|
||||||
return obj_to_response(response)
|
return obj_to_response(response)
|
||||||
|
|
||||||
async def get_wallet_balance(self, request) -> web.Response:
|
async def get_wallet_balance(self, request) -> web.Response:
|
||||||
|
|
||||||
|
balance = await self.wallet_node.wallet.get_confirmed_balance()
|
||||||
|
pending_balance = await self.wallet_node.wallet.get_unconfirmed_balance()
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
"success": True,
|
"success": True,
|
||||||
"confirmed_wallet_balance": 0,
|
"confirmed_wallet_balance": balance,
|
||||||
"unconfirmed_wallet_balance": 0,
|
"unconfirmed_wallet_balance": pending_balance,
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj_to_response(response)
|
return obj_to_response(response)
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,19 +115,19 @@ async def start_rpc_server():
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Keys not generated. Run python3 ./scripts/regenerate_keys.py."
|
"Keys not generated. Run python3 ./scripts/regenerate_keys.py."
|
||||||
)
|
)
|
||||||
wallet = await Wallet.create(config, key_config)
|
wallet_node = await WalletNode.create(config, key_config)
|
||||||
|
|
||||||
server = ChiaServer(9257, wallet, NodeType.WALLET)
|
server = ChiaServer(9257, wallet_node, NodeType.WALLET)
|
||||||
wallet.set_server(server)
|
wallet_node.set_server(server)
|
||||||
full_node_peer = PeerInfo(
|
full_node_peer = PeerInfo(
|
||||||
config["full_node_peer"]["host"], config["full_node_peer"]["port"]
|
config["full_node_peer"]["host"], config["full_node_peer"]["port"]
|
||||||
)
|
)
|
||||||
|
|
||||||
_ = await server.start_server("127.0.0.1", wallet._on_connect)
|
_ = await server.start_server("127.0.0.1", wallet_node._on_connect)
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
_ = await server.start_client(full_node_peer, None)
|
_ = await server.start_client(full_node_peer, None)
|
||||||
|
|
||||||
handler = RpcWalletApiHandler(wallet)
|
handler = RpcWalletApiHandler(wallet_node)
|
||||||
app = web.Application()
|
app = web.Application()
|
||||||
app.add_routes(
|
app.add_routes(
|
||||||
[
|
[
|
||||||
|
@ -155,8 +152,9 @@ async def start_rpc_server():
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
cleanup = await start_rpc_server()
|
cleanup = await start_rpc_server()
|
||||||
print('start running on {}')
|
print("start running on {}")
|
||||||
await cleanup()
|
await cleanup()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
|
@ -1,2 +0,0 @@
|
||||||
*.pyc
|
|
||||||
__pycache__/
|
|
|
@ -1,7 +0,0 @@
|
||||||
zerorpc
|
|
||||||
pyzmq
|
|
||||||
future
|
|
||||||
msgpack-python
|
|
||||||
gevent
|
|
||||||
pyinstaller
|
|
||||||
pypiwin32
|
|
|
@ -1,9 +1,11 @@
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
|
|
||||||
|
|
||||||
def load_clvm(filename):
|
def load_clvm(filename):
|
||||||
clvm_hex = pkg_resources.resource_string(__name__, "%s.hex" % filename).decode("utf8")
|
clvm_hex = pkg_resources.resource_string(__name__, "%s.hex" % filename).decode(
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
clvm_blob = bytes.fromhex(clvm_hex)
|
clvm_blob = bytes.fromhex(clvm_hex)
|
||||||
return Program.from_bytes(clvm_blob)
|
return Program.from_bytes(clvm_blob)
|
||||||
|
|
|
@ -10,11 +10,9 @@ require a delegated puzzle program, so in those cases, this is just what
|
||||||
the doctor ordered.
|
the doctor ordered.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import clvm
|
|
||||||
|
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
|
|
||||||
|
|
||||||
# contract:
|
# contract:
|
||||||
|
|
|
@ -10,12 +10,10 @@ the doctor ordered.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import clvm
|
|
||||||
|
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
|
|
||||||
|
|
||||||
def puzzle_for_pk(public_key):
|
def puzzle_for_pk(public_key):
|
||||||
|
|
|
@ -17,15 +17,17 @@ from typing import List
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
|
|
||||||
from . import p2_conditions
|
from . import p2_conditions
|
||||||
|
|
||||||
|
|
||||||
def puzzle_for_pk(public_key) -> Program:
|
def puzzle_for_pk(public_key) -> Program:
|
||||||
aggsig = ConditionOpcode.AGG_SIG[0]
|
aggsig = ConditionOpcode.AGG_SIG[0]
|
||||||
TEMPLATE = (f"(c (c (q {aggsig}) (c (q 0x%s) (c (sha256tree (f (a))) (q ())))) "
|
TEMPLATE = (
|
||||||
f"((c (f (a)) (f (r (a))))))")
|
f"(c (c (q {aggsig}) (c (q 0x%s) (c (sha256tree (f (a))) (q ())))) "
|
||||||
|
f"((c (f (a)) (f (r (a))))))"
|
||||||
|
)
|
||||||
return Program.to(binutils.assemble(TEMPLATE % public_key.hex()))
|
return Program.to(binutils.assemble(TEMPLATE % public_key.hex()))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import hashlib
|
||||||
|
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from clvm import run_program
|
from clvm import run_program
|
||||||
|
|
||||||
from .load_clvm import load_clvm
|
from .load_clvm import load_clvm
|
||||||
|
|
|
@ -5,7 +5,7 @@ This puzzle program is like p2_delegated_puzzle except instead of one public key
|
||||||
it includes N public keys, any M of which needs to sign the delegated puzzle.
|
it includes N public keys, any M of which needs to sign the delegated puzzle.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from .load_clvm import load_clvm
|
from .load_clvm import load_clvm
|
||||||
|
@ -15,10 +15,11 @@ puzzle_prog_template = load_clvm("make_puzzle_m_of_n_direct.clvm")
|
||||||
|
|
||||||
|
|
||||||
def puzzle_for_m_of_public_key_list(m, public_key_list):
|
def puzzle_for_m_of_public_key_list(m, public_key_list):
|
||||||
format_tuple = tuple(
|
format_tuple = tuple([
|
||||||
binutils.disassemble(Program.to(_))
|
binutils.disassemble(Program.to(_))
|
||||||
for _ in (puzzle_prog_template, m, public_key_list))
|
for _ in (puzzle_prog_template, m, public_key_list)
|
||||||
puzzle_src = "((c (q %s) (c (q %s) (c (q %s) (a)))))" % format_tuple
|
])
|
||||||
|
puzzle_src = "((c (q %s) (c (q %s) (c (q %s) (a)))))" % (format_tuple[0], format_tuple[1], format_tuple[2])
|
||||||
puzzle_prog = binutils.assemble(puzzle_src)
|
puzzle_prog = binutils.assemble(puzzle_src)
|
||||||
return Program.to(puzzle_prog)
|
return Program.to(puzzle_prog)
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,9 @@ In this puzzle program, the solution must be a reveal of the puzzle with the giv
|
||||||
hash along with its solution.
|
hash along with its solution.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import clvm
|
|
||||||
|
|
||||||
from clvm_tools import binutils
|
from clvm_tools import binutils
|
||||||
|
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
|
|
||||||
"""
|
"""
|
||||||
solution: (puzzle_reveal . solution_to_puzzle)
|
solution: (puzzle_reveal . solution_to_puzzle)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from src.util.condition_tools import ConditionOpcode
|
from src.util.condition_tools import ConditionOpcode
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def make_create_coin_condition(puzzle_hash, amount):
|
def make_create_coin_condition(puzzle_hash, amount):
|
||||||
return [ConditionOpcode.CREATE_COIN, puzzle_hash, amount]
|
return [ConditionOpcode.CREATE_COIN, puzzle_hash, amount]
|
||||||
|
|
||||||
|
@ -17,6 +16,7 @@ def make_assert_coin_consumed_condition(coin_name):
|
||||||
def make_assert_my_coin_id_condition(coin_name):
|
def make_assert_my_coin_id_condition(coin_name):
|
||||||
return [ConditionOpcode.ASSERT_MY_COIN_ID, coin_name]
|
return [ConditionOpcode.ASSERT_MY_COIN_ID, coin_name]
|
||||||
|
|
||||||
|
|
||||||
def make_assert_block_index_exceeds_condition(block_index):
|
def make_assert_block_index_exceeds_condition(block_index):
|
||||||
return [ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS, block_index]
|
return [ConditionOpcode.ASSERT_BLOCK_INDEX_EXCEEDS, block_index]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from src.types.hashable.coin import Coin
|
||||||
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
|
from src.types.sized_bytes import bytes32
|
||||||
|
from src.util.streamable import Streamable, streamable
|
||||||
|
from src.util.ints import uint32, uint64
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
@streamable
|
||||||
|
class TransactionRecord(Streamable):
|
||||||
|
"""
|
||||||
|
Used for storing transaction data and status in wallets
|
||||||
|
"""
|
||||||
|
|
||||||
|
confirmed_block_index: uint32
|
||||||
|
created_at_index: uint32
|
||||||
|
confirmed: bool
|
||||||
|
sent: bool
|
||||||
|
created_at_time: uint64
|
||||||
|
spend_bundle: Optional[SpendBundle]
|
||||||
|
additions: List[Coin]
|
||||||
|
removals: List[Coin]
|
||||||
|
|
||||||
|
def name(self) -> bytes32:
|
||||||
|
if self.spend_bundle:
|
||||||
|
return self.spend_bundle.name()
|
||||||
|
return self.get_hash()
|
|
@ -1,32 +1,21 @@
|
||||||
from pathlib import Path
|
from typing import Dict, Optional, List, Tuple
|
||||||
from typing import Dict, Optional, List, Set, Tuple
|
|
||||||
import clvm
|
import clvm
|
||||||
from blspy import ExtendedPrivateKey, PublicKey
|
from blspy import ExtendedPrivateKey, PublicKey
|
||||||
import logging
|
import logging
|
||||||
import src.protocols.wallet_protocol
|
|
||||||
from src.full_node import OutboundMessageGenerator
|
|
||||||
from src.protocols.wallet_protocol import ProofHash
|
|
||||||
from src.protocols.full_node_protocol import RespondTransaction
|
|
||||||
from src.server.outbound_message import OutboundMessage, NodeType, Message, Delivery
|
from src.server.outbound_message import OutboundMessage, NodeType, Message, Delivery
|
||||||
from src.server.server import ChiaServer
|
from src.server.server import ChiaServer
|
||||||
from src.types.full_block import additions_for_npc
|
from src.protocols import full_node_protocol
|
||||||
from src.types.hashable.BLSSignature import BLSSignature
|
from src.types.hashable.BLSSignature import BLSSignature
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin_solution import CoinSolution
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.program import Program
|
||||||
from src.types.hashable.CoinSolution import CoinSolution
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.types.hashable.Program import Program
|
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
|
||||||
from src.types.name_puzzle_condition import NPC
|
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.hash import std_hash
|
|
||||||
from src.util.api_decorators import api_request
|
|
||||||
from src.util.condition_tools import (
|
from src.util.condition_tools import (
|
||||||
conditions_for_solution,
|
conditions_for_solution,
|
||||||
conditions_by_opcode,
|
conditions_by_opcode,
|
||||||
hash_key_pairs_for_conditions_dict,
|
hash_key_pairs_for_conditions_dict,
|
||||||
)
|
)
|
||||||
from src.util.ints import uint32, uint64
|
from src.util.ints import uint64
|
||||||
from src.util.mempool_check_conditions import get_name_puzzle_conditions
|
|
||||||
from src.wallet.BLSPrivateKey import BLSPrivateKey
|
from src.wallet.BLSPrivateKey import BLSPrivateKey
|
||||||
from src.wallet.puzzles.p2_conditions import puzzle_for_conditions
|
from src.wallet.puzzles.p2_conditions import puzzle_for_conditions
|
||||||
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
|
from src.wallet.puzzles.p2_delegated_puzzle import puzzle_for_pk
|
||||||
|
@ -36,7 +25,8 @@ from src.wallet.puzzles.puzzle_utils import (
|
||||||
make_assert_coin_consumed_condition,
|
make_assert_coin_consumed_condition,
|
||||||
make_create_coin_condition,
|
make_create_coin_condition,
|
||||||
)
|
)
|
||||||
from src.wallet.wallet_store import WalletStore
|
|
||||||
|
from src.wallet.wallet_state_manager import WalletStateManager
|
||||||
|
|
||||||
|
|
||||||
class Wallet:
|
class Wallet:
|
||||||
|
@ -46,22 +36,8 @@ class Wallet:
|
||||||
server: Optional[ChiaServer]
|
server: Optional[ChiaServer]
|
||||||
next_address: int = 0
|
next_address: int = 0
|
||||||
pubkey_num_lookup: Dict[bytes, int]
|
pubkey_num_lookup: Dict[bytes, int]
|
||||||
tmp_coins: Set[Coin]
|
wallet_state_manager: WalletStateManager
|
||||||
wallet_store: WalletStore
|
|
||||||
header_hash: List[bytes32]
|
|
||||||
start_index: int
|
|
||||||
|
|
||||||
unconfirmed_removals: Set[Coin]
|
|
||||||
unconfirmed_removal_amount: int
|
|
||||||
|
|
||||||
unconfirmed_additions: Set[Coin]
|
|
||||||
unconfirmed_addition_amount: int
|
|
||||||
|
|
||||||
# This dict maps coin_id to SpendBundle, it will contain duplicate values by design
|
|
||||||
coin_spend_bundle_map: Dict[bytes32, SpendBundle]
|
|
||||||
|
|
||||||
# Spendbundle_ID : Spendbundle
|
|
||||||
pending_spend_bundles: Dict[bytes32, SpendBundle]
|
|
||||||
log: logging.Logger
|
log: logging.Logger
|
||||||
|
|
||||||
# TODO Don't allow user to send tx until wallet is synced
|
# TODO Don't allow user to send tx until wallet is synced
|
||||||
|
@ -71,9 +47,13 @@ class Wallet:
|
||||||
send_queue: Dict[bytes32, SpendBundle]
|
send_queue: Dict[bytes32, SpendBundle]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def create(config: Dict, key_config: Dict, name: str = None):
|
async def create(
|
||||||
|
config: Dict,
|
||||||
|
key_config: Dict,
|
||||||
|
wallet_state_manager: WalletStateManager,
|
||||||
|
name: str = None,
|
||||||
|
):
|
||||||
self = Wallet()
|
self = Wallet()
|
||||||
print("init wallet")
|
|
||||||
self.config = config
|
self.config = config
|
||||||
self.key_config = key_config
|
self.key_config = key_config
|
||||||
sk_hex = self.key_config["wallet_sk"]
|
sk_hex = self.key_config["wallet_sk"]
|
||||||
|
@ -83,22 +63,9 @@ class Wallet:
|
||||||
else:
|
else:
|
||||||
self.log = logging.getLogger(__name__)
|
self.log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
self.wallet_state_manager = wallet_state_manager
|
||||||
self.pubkey_num_lookup = {}
|
self.pubkey_num_lookup = {}
|
||||||
self.tmp_coins = set()
|
|
||||||
pub_hex = self.private_key.get_public_key().serialize().hex()
|
|
||||||
path = Path(f"wallet_db_{pub_hex}.db")
|
|
||||||
self.wallet_store = await WalletStore.create(path)
|
|
||||||
self.header_hash = []
|
|
||||||
self.unconfirmed_additions = set()
|
|
||||||
self.unconfirmed_removals = set()
|
|
||||||
self.pending_spend_bundles = {}
|
|
||||||
self.coin_spend_bundle_map = {}
|
|
||||||
self.unconfirmed_addition_amount = 0
|
|
||||||
self.unconfirmed_removal_amount = 0
|
|
||||||
|
|
||||||
self.synced = False
|
|
||||||
|
|
||||||
self.send_queue = {}
|
|
||||||
self.server = None
|
self.server = None
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
@ -107,23 +74,14 @@ class Wallet:
|
||||||
pubkey = self.private_key.public_child(self.next_address).get_public_key()
|
pubkey = self.private_key.public_child(self.next_address).get_public_key()
|
||||||
self.pubkey_num_lookup[pubkey.serialize()] = self.next_address
|
self.pubkey_num_lookup[pubkey.serialize()] = self.next_address
|
||||||
self.next_address = self.next_address + 1
|
self.next_address = self.next_address + 1
|
||||||
|
self.wallet_state_manager.next_address = self.next_address
|
||||||
return pubkey
|
return pubkey
|
||||||
|
|
||||||
async def get_confirmed_balance(self) -> uint64:
|
async def get_confirmed_balance(self) -> uint64:
|
||||||
record_list: Set[
|
return await self.wallet_state_manager.get_confirmed_balance()
|
||||||
CoinRecord
|
|
||||||
] = await self.wallet_store.get_coin_records_by_spent(False)
|
|
||||||
amount: uint64 = uint64(0)
|
|
||||||
|
|
||||||
for record in record_list:
|
|
||||||
amount = uint64(amount + record.coin.amount)
|
|
||||||
|
|
||||||
return uint64(amount)
|
|
||||||
|
|
||||||
async def get_unconfirmed_balance(self) -> uint64:
|
async def get_unconfirmed_balance(self) -> uint64:
|
||||||
confirmed = await self.get_confirmed_balance()
|
return await self.wallet_state_manager.get_unconfirmed_balance()
|
||||||
result = confirmed - self.unconfirmed_removal_amount + self.unconfirmed_addition_amount
|
|
||||||
return uint64(result)
|
|
||||||
|
|
||||||
def can_generate_puzzle_hash(self, hash: bytes32) -> bool:
|
def can_generate_puzzle_hash(self, hash: bytes32) -> bool:
|
||||||
return any(
|
return any(
|
||||||
|
@ -147,50 +105,9 @@ class Wallet:
|
||||||
def get_new_puzzlehash(self) -> bytes32:
|
def get_new_puzzlehash(self) -> bytes32:
|
||||||
puzzle: Program = self.get_new_puzzle()
|
puzzle: Program = self.get_new_puzzle()
|
||||||
puzzlehash: bytes32 = puzzle.get_hash()
|
puzzlehash: bytes32 = puzzle.get_hash()
|
||||||
|
self.wallet_state_manager.puzzlehash_set.add(puzzlehash)
|
||||||
return puzzlehash
|
return puzzlehash
|
||||||
|
|
||||||
async def select_coins(self, amount) -> Optional[Set[Coin]]:
|
|
||||||
|
|
||||||
if amount > await self.get_unconfirmed_balance():
|
|
||||||
return None
|
|
||||||
|
|
||||||
unspent: Set[
|
|
||||||
CoinRecord
|
|
||||||
] = await self.wallet_store.get_coin_records_by_spent(False)
|
|
||||||
sum = 0
|
|
||||||
used_coins: Set = set()
|
|
||||||
|
|
||||||
"""
|
|
||||||
Try to use coins from the store, if there isn't enough of "unused"
|
|
||||||
coins use change coins that are not confirmed yet
|
|
||||||
"""
|
|
||||||
for coinrecord in unspent:
|
|
||||||
if sum >= amount:
|
|
||||||
break
|
|
||||||
if coinrecord.coin.name in self.unconfirmed_removals:
|
|
||||||
continue
|
|
||||||
sum += coinrecord.coin.amount
|
|
||||||
used_coins.add(coinrecord.coin)
|
|
||||||
|
|
||||||
"""
|
|
||||||
This happens when we couldn't use one of the coins because it's already used
|
|
||||||
but unconfirmed, and we are waiting for the change. (unconfirmed_additions)
|
|
||||||
"""
|
|
||||||
if sum < amount:
|
|
||||||
for coin in self.unconfirmed_additions:
|
|
||||||
if sum > amount:
|
|
||||||
break
|
|
||||||
if coin.name in self.unconfirmed_removals:
|
|
||||||
continue
|
|
||||||
sum += coin.amount
|
|
||||||
used_coins.add(coin)
|
|
||||||
|
|
||||||
if sum >= amount:
|
|
||||||
return used_coins
|
|
||||||
else:
|
|
||||||
# This shouldn't happen because of: if amount > self.get_unconfirmed_balance():
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_server(self, server: ChiaServer):
|
def set_server(self, server: ChiaServer):
|
||||||
self.server = server
|
self.server = server
|
||||||
|
|
||||||
|
@ -227,7 +144,7 @@ class Wallet:
|
||||||
async def generate_unsigned_transaction(
|
async def generate_unsigned_transaction(
|
||||||
self, amount: int, newpuzzlehash: bytes32, fee: int = 0
|
self, amount: int, newpuzzlehash: bytes32, fee: int = 0
|
||||||
) -> List[Tuple[Program, CoinSolution]]:
|
) -> List[Tuple[Program, CoinSolution]]:
|
||||||
utxos = await self.select_coins(amount + fee)
|
utxos = await self.wallet_state_manager.select_coins(amount + fee)
|
||||||
if utxos is None:
|
if utxos is None:
|
||||||
return []
|
return []
|
||||||
spends: List[Tuple[Program, CoinSolution]] = []
|
spends: List[Tuple[Program, CoinSolution]] = []
|
||||||
|
@ -246,8 +163,7 @@ class Wallet:
|
||||||
if change > 0:
|
if change > 0:
|
||||||
changepuzzlehash = self.get_new_puzzlehash()
|
changepuzzlehash = self.get_new_puzzlehash()
|
||||||
primaries.append({"puzzlehash": changepuzzlehash, "amount": change})
|
primaries.append({"puzzlehash": changepuzzlehash, "amount": change})
|
||||||
# add change coin into temp_utxo set
|
|
||||||
self.tmp_coins.add(Coin(coin.name(), changepuzzlehash, uint64(change)))
|
|
||||||
solution = self.make_solution(primaries=primaries)
|
solution = self.make_solution(primaries=primaries)
|
||||||
output_created = True
|
output_created = True
|
||||||
else:
|
else:
|
||||||
|
@ -286,6 +202,7 @@ class Wallet:
|
||||||
async def generate_signed_transaction(
|
async def generate_signed_transaction(
|
||||||
self, amount, newpuzzlehash, fee: int = 0
|
self, amount, newpuzzlehash, fee: int = 0
|
||||||
) -> Optional[SpendBundle]:
|
) -> Optional[SpendBundle]:
|
||||||
|
""" Use this to generate transaction. """
|
||||||
transaction = await self.generate_unsigned_transaction(
|
transaction = await self.generate_unsigned_transaction(
|
||||||
amount, newpuzzlehash, fee
|
amount, newpuzzlehash, fee
|
||||||
)
|
)
|
||||||
|
@ -293,146 +210,17 @@ class Wallet:
|
||||||
return None
|
return None
|
||||||
return self.sign_transaction(transaction)
|
return self.sign_transaction(transaction)
|
||||||
|
|
||||||
async def coin_removed(self, coin_name: bytes32, index: uint32):
|
|
||||||
self.log.info("remove coin")
|
|
||||||
await self.wallet_store.set_spent(coin_name, index)
|
|
||||||
|
|
||||||
async def coin_added(self, coin: Coin, index: uint32, coinbase: bool):
|
|
||||||
self.log.info("add coin")
|
|
||||||
coin_record: CoinRecord = CoinRecord(coin, index, uint32(0), False, coinbase)
|
|
||||||
await self.wallet_store.add_coin_record(coin_record)
|
|
||||||
|
|
||||||
async def _on_connect(self) -> OutboundMessageGenerator:
|
|
||||||
"""
|
|
||||||
Whenever we connect to a FullNode we request new proof_hashes by sending last proof hash we have
|
|
||||||
"""
|
|
||||||
self.log.info(f"Requesting proof hashes")
|
|
||||||
request = ProofHash(std_hash(b"deadbeef"))
|
|
||||||
yield OutboundMessage(
|
|
||||||
NodeType.FULL_NODE,
|
|
||||||
Message("request_proof_hashes", request),
|
|
||||||
Delivery.BROADCAST,
|
|
||||||
)
|
|
||||||
|
|
||||||
@api_request
|
|
||||||
async def proof_hash(
|
|
||||||
self, request: src.protocols.wallet_protocol.ProofHash
|
|
||||||
) -> OutboundMessageGenerator:
|
|
||||||
"""
|
|
||||||
Received a proof hash from the FullNode
|
|
||||||
"""
|
|
||||||
self.log.info(f"Received a new proof hash: {request}")
|
|
||||||
reply_request = ProofHash(std_hash(b"a"))
|
|
||||||
# TODO Store and decide if we want full proof for this proof hash
|
|
||||||
yield OutboundMessage(
|
|
||||||
NodeType.FULL_NODE,
|
|
||||||
Message("request_full_proof_for_hash", reply_request),
|
|
||||||
Delivery.RESPOND,
|
|
||||||
)
|
|
||||||
|
|
||||||
@api_request
|
|
||||||
async def full_proof_for_hash(
|
|
||||||
self, request: src.protocols.wallet_protocol.FullProofForHash
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
We've received a full proof for hash we requested
|
|
||||||
"""
|
|
||||||
# TODO Validate full proof
|
|
||||||
self.log.info(f"Received new proof: {request}")
|
|
||||||
|
|
||||||
@api_request
|
|
||||||
async def received_body(self, response: src.protocols.wallet_protocol.RespondBody):
|
|
||||||
"""
|
|
||||||
Called when body is received from the FullNode
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Retry sending queued up transactions
|
|
||||||
await self.retry_send_queue()
|
|
||||||
|
|
||||||
additions: List[Coin] = []
|
|
||||||
|
|
||||||
if self.can_generate_puzzle_hash(response.body.coinbase.puzzle_hash):
|
|
||||||
await self.coin_added(response.body.coinbase, response.height, True)
|
|
||||||
if self.can_generate_puzzle_hash(response.body.fees_coin.puzzle_hash):
|
|
||||||
await self.coin_added(response.body.fees_coin, response.height, True)
|
|
||||||
|
|
||||||
npc_list: List[NPC]
|
|
||||||
if response.body.transactions:
|
|
||||||
error, npc_list, cost = await get_name_puzzle_conditions(
|
|
||||||
response.body.transactions
|
|
||||||
)
|
|
||||||
|
|
||||||
additions.extend(additions_for_npc(npc_list))
|
|
||||||
|
|
||||||
for added_coin in additions:
|
|
||||||
if self.can_generate_puzzle_hash(added_coin.puzzle_hash):
|
|
||||||
await self.coin_added(added_coin, response.height, False)
|
|
||||||
|
|
||||||
for npc in npc_list:
|
|
||||||
if self.can_generate_puzzle_hash(npc.puzzle_hash):
|
|
||||||
await self.coin_removed(npc.coin_name, response.height)
|
|
||||||
|
|
||||||
@api_request
|
|
||||||
async def new_tip(self, header: src.protocols.wallet_protocol.Header):
|
|
||||||
self.log.info("new tip received")
|
|
||||||
|
|
||||||
async def retry_send_queue(self):
|
|
||||||
for key, val in self.send_queue:
|
|
||||||
await self._send_transaction(val)
|
|
||||||
|
|
||||||
def remove_from_queue(self, spendbundle_id: bytes32):
|
|
||||||
if spendbundle_id in self.send_queue:
|
|
||||||
del self.send_queue[spendbundle_id]
|
|
||||||
|
|
||||||
async def push_transaction(self, spend_bundle: SpendBundle):
|
async def push_transaction(self, spend_bundle: SpendBundle):
|
||||||
""" Use this API to make transactions. """
|
""" Use this API to send transactions. """
|
||||||
self.send_queue[spend_bundle.name()] = spend_bundle
|
await self.wallet_state_manager.add_pending_transaction(spend_bundle)
|
||||||
additions: List[Coin] = spend_bundle.additions()
|
|
||||||
removals: List[Coin] = spend_bundle.removals()
|
|
||||||
|
|
||||||
addition_amount = 0
|
|
||||||
for coin in additions:
|
|
||||||
if self.can_generate_puzzle_hash(coin.puzzle_hash):
|
|
||||||
self.unconfirmed_additions.add(coin)
|
|
||||||
self.coin_spend_bundle_map[coin.name()] = spend_bundle
|
|
||||||
addition_amount += coin.amount
|
|
||||||
|
|
||||||
removal_amount = 0
|
|
||||||
for coin in removals:
|
|
||||||
self.unconfirmed_removals.add(coin)
|
|
||||||
self.coin_spend_bundle_map[coin.name()] = spend_bundle
|
|
||||||
removal_amount += coin.amount
|
|
||||||
|
|
||||||
# Update unconfirmed state
|
|
||||||
self.unconfirmed_removal_amount += removal_amount
|
|
||||||
self.unconfirmed_addition_amount += addition_amount
|
|
||||||
|
|
||||||
self.pending_spend_bundles[spend_bundle.name()] = spend_bundle
|
|
||||||
await self._send_transaction(spend_bundle)
|
await self._send_transaction(spend_bundle)
|
||||||
|
|
||||||
async def _send_transaction(self, spend_bundle: SpendBundle):
|
async def _send_transaction(self, spend_bundle: SpendBundle):
|
||||||
""" Sends spendbundle to connected full Nodes."""
|
|
||||||
|
|
||||||
msg = OutboundMessage(
|
|
||||||
NodeType.FULL_NODE,
|
|
||||||
Message("respond_transaction", RespondTransaction(spend_bundle)),
|
|
||||||
Delivery.BROADCAST,
|
|
||||||
)
|
|
||||||
if self.server:
|
if self.server:
|
||||||
|
msg = OutboundMessage(
|
||||||
|
NodeType.FULL_NODE,
|
||||||
|
Message("respond_transaction", full_node_protocol.RespondTransaction(spend_bundle)),
|
||||||
|
Delivery.BROADCAST,
|
||||||
|
)
|
||||||
async for reply in self.server.push_message(msg):
|
async for reply in self.server.push_message(msg):
|
||||||
self.log.info(reply)
|
self.log.info(reply)
|
||||||
|
|
||||||
@api_request
|
|
||||||
async def transaction_ack(self, ack: src.protocols.wallet_protocol.TransactionAck):
|
|
||||||
if ack.status:
|
|
||||||
self.remove_from_queue(ack.txid)
|
|
||||||
self.log.info(f"SpendBundle has been received by the FullNode. id: {id}")
|
|
||||||
else:
|
|
||||||
self.log.info(f"SpendBundle has been rejected by the FullNode. id: {id}")
|
|
||||||
|
|
||||||
async def requestLCA(self):
|
|
||||||
msg = OutboundMessage(
|
|
||||||
NodeType.FULL_NODE, Message("request_lca", None), Delivery.BROADCAST,
|
|
||||||
)
|
|
||||||
async for reply in self.server.push_message(msg):
|
|
||||||
self.log.info(reply)
|
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Optional, List
|
||||||
|
from blspy import ExtendedPrivateKey
|
||||||
|
import logging
|
||||||
|
import src.protocols.wallet_protocol
|
||||||
|
from src.full_node.full_node import OutboundMessageGenerator
|
||||||
|
from src.protocols.wallet_protocol import ProofHash
|
||||||
|
from src.server.outbound_message import OutboundMessage, NodeType, Message, Delivery
|
||||||
|
from src.server.server import ChiaServer
|
||||||
|
from src.types.full_block import additions_for_npc
|
||||||
|
from src.types.hashable.coin import Coin
|
||||||
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
|
from src.types.name_puzzle_condition import NPC
|
||||||
|
from src.types.sized_bytes import bytes32
|
||||||
|
from src.util.hash import std_hash
|
||||||
|
from src.util.api_decorators import api_request
|
||||||
|
from src.util.ints import uint32
|
||||||
|
from src.util.mempool_check_conditions import get_name_puzzle_conditions
|
||||||
|
from src.wallet.wallet import Wallet
|
||||||
|
from src.wallet.wallet_state_manager import WalletStateManager
|
||||||
|
from src.wallet.wallet_store import WalletStore
|
||||||
|
from src.wallet.wallet_transaction_store import WalletTransactionStore
|
||||||
|
|
||||||
|
|
||||||
|
class WalletNode:
|
||||||
|
private_key: ExtendedPrivateKey
|
||||||
|
key_config: Dict
|
||||||
|
config: Dict
|
||||||
|
server: Optional[ChiaServer]
|
||||||
|
wallet_store: WalletStore
|
||||||
|
wallet_state_manager: WalletStateManager
|
||||||
|
header_hash: List[bytes32]
|
||||||
|
start_index: int
|
||||||
|
log: logging.Logger
|
||||||
|
wallet: Wallet
|
||||||
|
tx_store: WalletTransactionStore
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def create(config: Dict, key_config: Dict, name: str = None):
|
||||||
|
self = WalletNode()
|
||||||
|
self.config = config
|
||||||
|
self.key_config = key_config
|
||||||
|
sk_hex = self.key_config["wallet_sk"]
|
||||||
|
self.private_key = ExtendedPrivateKey.from_bytes(bytes.fromhex(sk_hex))
|
||||||
|
if name:
|
||||||
|
self.log = logging.getLogger(name)
|
||||||
|
else:
|
||||||
|
self.log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
pub_hex = self.private_key.get_public_key().serialize().hex()
|
||||||
|
path = Path(f"wallet_db_{pub_hex}.db")
|
||||||
|
self.wallet_store = await WalletStore.create(path)
|
||||||
|
self.tx_store = await WalletTransactionStore.create(path)
|
||||||
|
|
||||||
|
self.wallet_state_manager = await WalletStateManager.create(
|
||||||
|
config, key_config, self.wallet_store, self.tx_store
|
||||||
|
)
|
||||||
|
self.wallet = await Wallet.create(config, key_config, self.wallet_state_manager)
|
||||||
|
|
||||||
|
self.server = None
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_server(self, server: ChiaServer):
|
||||||
|
self.server = server
|
||||||
|
self.wallet.set_server(server)
|
||||||
|
|
||||||
|
async def _on_connect(self) -> OutboundMessageGenerator:
|
||||||
|
"""
|
||||||
|
Whenever we connect to a FullNode we request new proof_hashes by sending last proof hash we have
|
||||||
|
"""
|
||||||
|
self.log.info(f"Requesting proof hashes")
|
||||||
|
request = ProofHash(std_hash(b"deadbeef"))
|
||||||
|
yield OutboundMessage(
|
||||||
|
NodeType.FULL_NODE,
|
||||||
|
Message("request_proof_hashes", request),
|
||||||
|
Delivery.BROADCAST,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def proof_hash(
|
||||||
|
self, request: src.protocols.wallet_protocol.ProofHash
|
||||||
|
) -> OutboundMessageGenerator:
|
||||||
|
"""
|
||||||
|
Received a proof hash from the FullNode
|
||||||
|
"""
|
||||||
|
self.log.info(f"Received a new proof hash: {request}")
|
||||||
|
reply_request = ProofHash(std_hash(b"a"))
|
||||||
|
# TODO Store and decide if we want full proof for this proof hash
|
||||||
|
yield OutboundMessage(
|
||||||
|
NodeType.FULL_NODE,
|
||||||
|
Message("request_full_proof_for_hash", reply_request),
|
||||||
|
Delivery.RESPOND,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def full_proof_for_hash(
|
||||||
|
self, request: src.protocols.wallet_protocol.FullProofForHash
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
We've received a full proof for hash we requested
|
||||||
|
"""
|
||||||
|
# TODO Validate full proof
|
||||||
|
self.log.info(f"Received new proof: {request}")
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def received_body(self, response: src.protocols.wallet_protocol.RespondBody):
|
||||||
|
"""
|
||||||
|
Called when body is received from the FullNode
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Retry sending queued up transactions
|
||||||
|
await self.retry_send_queue()
|
||||||
|
|
||||||
|
additions: List[Coin] = []
|
||||||
|
|
||||||
|
if self.wallet.can_generate_puzzle_hash(response.body.coinbase.puzzle_hash):
|
||||||
|
await self.wallet_state_manager.coin_added(
|
||||||
|
response.body.coinbase, response.height, True
|
||||||
|
)
|
||||||
|
if self.wallet.can_generate_puzzle_hash(response.body.fees_coin.puzzle_hash):
|
||||||
|
await self.wallet_state_manager.coin_added(
|
||||||
|
response.body.fees_coin, response.height, True
|
||||||
|
)
|
||||||
|
|
||||||
|
npc_list: List[NPC]
|
||||||
|
if response.body.transactions:
|
||||||
|
error, npc_list, cost = get_name_puzzle_conditions(
|
||||||
|
response.body.transactions
|
||||||
|
)
|
||||||
|
|
||||||
|
additions.extend(additions_for_npc(npc_list))
|
||||||
|
|
||||||
|
for added_coin in additions:
|
||||||
|
if self.wallet.can_generate_puzzle_hash(added_coin.puzzle_hash):
|
||||||
|
await self.wallet_state_manager.coin_added(
|
||||||
|
added_coin, response.height, False
|
||||||
|
)
|
||||||
|
|
||||||
|
for npc in npc_list:
|
||||||
|
if self.wallet.can_generate_puzzle_hash(npc.puzzle_hash):
|
||||||
|
await self.wallet_state_manager.coin_removed(
|
||||||
|
npc.coin_name, response.height
|
||||||
|
)
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def new_lca(self, header: src.protocols.wallet_protocol.Header):
|
||||||
|
self.log.info("new tip received")
|
||||||
|
|
||||||
|
async def retry_send_queue(self):
|
||||||
|
records = await self.wallet_state_manager.get_send_queue()
|
||||||
|
for record in records:
|
||||||
|
if record.spend_bundle:
|
||||||
|
await self._send_transaction(record.spend_bundle)
|
||||||
|
|
||||||
|
async def _send_transaction(self, spend_bundle: SpendBundle):
|
||||||
|
""" Sends spendbundle to connected full Nodes."""
|
||||||
|
await self.wallet_state_manager.add_pending_transaction(spend_bundle)
|
||||||
|
|
||||||
|
msg = OutboundMessage(
|
||||||
|
NodeType.FULL_NODE,
|
||||||
|
Message("wallet_transaction", spend_bundle),
|
||||||
|
Delivery.BROADCAST,
|
||||||
|
)
|
||||||
|
if self.server:
|
||||||
|
async for reply in self.server.push_message(msg):
|
||||||
|
self.log.info(reply)
|
||||||
|
|
||||||
|
async def _request_add_list(self, height: uint32, header_hash: bytes32):
|
||||||
|
obj = src.protocols.wallet_protocol.RequestAdditions(height, header_hash)
|
||||||
|
msg = OutboundMessage(
|
||||||
|
NodeType.FULL_NODE, Message("request_additions", obj), Delivery.BROADCAST,
|
||||||
|
)
|
||||||
|
if self.server:
|
||||||
|
async for reply in self.server.push_message(msg):
|
||||||
|
self.log.info(reply)
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def response_additions(
|
||||||
|
self, response: src.protocols.wallet_protocol.Additions
|
||||||
|
):
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def response_additions_rejected(
|
||||||
|
self, response: src.protocols.wallet_protocol.RequestAdditions
|
||||||
|
):
|
||||||
|
print(f"request rejected {response}")
|
||||||
|
|
||||||
|
@api_request
|
||||||
|
async def transaction_ack(self, ack: src.protocols.wallet_protocol.TransactionAck):
|
||||||
|
if ack.status:
|
||||||
|
await self.wallet_state_manager.remove_from_queue(ack.txid)
|
||||||
|
self.log.info(f"SpendBundle has been received by the FullNode. id: {id}")
|
||||||
|
else:
|
||||||
|
self.log.info(f"SpendBundle has been rejected by the FullNode. id: {id}")
|
||||||
|
|
||||||
|
async def requestLCA(self):
|
||||||
|
msg = OutboundMessage(
|
||||||
|
NodeType.FULL_NODE, Message("request_lca", None), Delivery.BROADCAST,
|
||||||
|
)
|
||||||
|
async for reply in self.server.push_message(msg):
|
||||||
|
self.log.info(reply)
|
|
@ -0,0 +1,188 @@
|
||||||
|
import time
|
||||||
|
from typing import Dict, Optional, List, Set
|
||||||
|
import logging
|
||||||
|
from src.types.hashable.coin import Coin
|
||||||
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
|
from src.types.sized_bytes import bytes32
|
||||||
|
from src.util.ints import uint32, uint64
|
||||||
|
from src.wallet.transaction_record import TransactionRecord
|
||||||
|
from src.wallet.wallet_store import WalletStore
|
||||||
|
from src.wallet.wallet_transaction_store import WalletTransactionStore
|
||||||
|
|
||||||
|
|
||||||
|
class WalletStateManager:
|
||||||
|
key_config: Dict
|
||||||
|
config: Dict
|
||||||
|
wallet_store: WalletStore
|
||||||
|
tx_store: WalletTransactionStore
|
||||||
|
header_hash: List[bytes32]
|
||||||
|
start_index: int
|
||||||
|
next_address: int
|
||||||
|
|
||||||
|
log: logging.Logger
|
||||||
|
|
||||||
|
# TODO Don't allow user to send tx until wallet is synced
|
||||||
|
synced: bool
|
||||||
|
puzzlehash_set: set
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def create(
|
||||||
|
config: Dict,
|
||||||
|
key_config: Dict,
|
||||||
|
wallet_store: WalletStore,
|
||||||
|
tx_store: WalletTransactionStore,
|
||||||
|
name: str = None,
|
||||||
|
):
|
||||||
|
self = WalletStateManager()
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
if name:
|
||||||
|
self.log = logging.getLogger(name)
|
||||||
|
else:
|
||||||
|
self.log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
self.header_hash = []
|
||||||
|
self.wallet_store = wallet_store
|
||||||
|
self.tx_store = tx_store
|
||||||
|
self.synced = False
|
||||||
|
self.next_address = 0
|
||||||
|
|
||||||
|
self.puzzlehash_set = set()
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def get_confirmed_balance(self) -> uint64:
|
||||||
|
record_list: Set[
|
||||||
|
CoinRecord
|
||||||
|
] = await self.wallet_store.get_coin_records_by_spent(False)
|
||||||
|
amount: uint64 = uint64(0)
|
||||||
|
|
||||||
|
for record in record_list:
|
||||||
|
amount = uint64(amount + record.coin.amount)
|
||||||
|
|
||||||
|
return uint64(amount)
|
||||||
|
|
||||||
|
async def get_unconfirmed_balance(self) -> uint64:
|
||||||
|
confirmed = await self.get_confirmed_balance()
|
||||||
|
unconfirmed_tx = await self.tx_store.get_not_confirmed()
|
||||||
|
addition_amount = 0
|
||||||
|
removal_amount = 0
|
||||||
|
|
||||||
|
for record in unconfirmed_tx:
|
||||||
|
for coin in record.additions:
|
||||||
|
if coin.puzzle_hash in self.puzzlehash_set:
|
||||||
|
addition_amount += coin.amount
|
||||||
|
for coin in record.removals:
|
||||||
|
removal_amount += coin.amount
|
||||||
|
|
||||||
|
result = confirmed - removal_amount + addition_amount
|
||||||
|
return uint64(result)
|
||||||
|
|
||||||
|
async def unconfirmed_additions(self) -> Dict[bytes32, Coin]:
|
||||||
|
additions: Dict[bytes32, Coin] = {}
|
||||||
|
unconfirmed_tx = await self.tx_store.get_not_confirmed()
|
||||||
|
for record in unconfirmed_tx:
|
||||||
|
for coin in record.additions:
|
||||||
|
additions[coin.name()] = coin
|
||||||
|
return additions
|
||||||
|
|
||||||
|
async def unconfirmed_removals(self) -> Dict[bytes32, Coin]:
|
||||||
|
removals: Dict[bytes32, Coin] = {}
|
||||||
|
unconfirmed_tx = await self.tx_store.get_not_confirmed()
|
||||||
|
for record in unconfirmed_tx:
|
||||||
|
for coin in record.removals:
|
||||||
|
removals[coin.name()] = coin
|
||||||
|
return removals
|
||||||
|
|
||||||
|
async def select_coins(self, amount) -> Optional[Set[Coin]]:
|
||||||
|
|
||||||
|
if amount > await self.get_unconfirmed_balance():
|
||||||
|
return None
|
||||||
|
|
||||||
|
unspent: Set[CoinRecord] = await self.wallet_store.get_coin_records_by_spent(
|
||||||
|
False
|
||||||
|
)
|
||||||
|
sum = 0
|
||||||
|
used_coins: Set = set()
|
||||||
|
|
||||||
|
"""
|
||||||
|
Try to use coins from the store, if there isn't enough of "unused"
|
||||||
|
coins use change coins that are not confirmed yet
|
||||||
|
"""
|
||||||
|
for coinrecord in unspent:
|
||||||
|
if sum >= amount:
|
||||||
|
break
|
||||||
|
if coinrecord.coin.name in await self.unconfirmed_removals():
|
||||||
|
continue
|
||||||
|
sum += coinrecord.coin.amount
|
||||||
|
used_coins.add(coinrecord.coin)
|
||||||
|
|
||||||
|
"""
|
||||||
|
This happens when we couldn't use one of the coins because it's already used
|
||||||
|
but unconfirmed, and we are waiting for the change. (unconfirmed_additions)
|
||||||
|
"""
|
||||||
|
if sum < amount:
|
||||||
|
for coin in (await self.unconfirmed_additions()).values():
|
||||||
|
if sum > amount:
|
||||||
|
break
|
||||||
|
if coin.name in (await self.unconfirmed_removals()).values():
|
||||||
|
continue
|
||||||
|
sum += coin.amount
|
||||||
|
used_coins.add(coin)
|
||||||
|
|
||||||
|
if sum >= amount:
|
||||||
|
return used_coins
|
||||||
|
else:
|
||||||
|
# This shouldn't happen because of: if amount > self.get_unconfirmed_balance():
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def coin_removed(self, coin_name: bytes32, index: uint32):
|
||||||
|
"""
|
||||||
|
Called when coin gets spent
|
||||||
|
"""
|
||||||
|
await self.wallet_store.set_spent(coin_name, index)
|
||||||
|
|
||||||
|
async def coin_added(self, coin: Coin, index: uint32, coinbase: bool):
|
||||||
|
"""
|
||||||
|
Adding coin to the db
|
||||||
|
"""
|
||||||
|
coin_record: CoinRecord = CoinRecord(coin, index, uint32(0), False, coinbase)
|
||||||
|
await self.wallet_store.add_coin_record(coin_record)
|
||||||
|
|
||||||
|
async def add_pending_transaction(self, spend_bundle: SpendBundle):
|
||||||
|
"""
|
||||||
|
Called from wallet_node before new transaction is sent to the full_node
|
||||||
|
"""
|
||||||
|
now = uint64(int(time.time()))
|
||||||
|
add_list: List[Coin] = []
|
||||||
|
rem_list: List[Coin] = []
|
||||||
|
for add in spend_bundle.additions():
|
||||||
|
add_list.append(add)
|
||||||
|
for rem in spend_bundle.removals():
|
||||||
|
rem_list.append(rem)
|
||||||
|
|
||||||
|
# Wallet node will use this queue to retry sending this transaction until full nodes receives it
|
||||||
|
tx_record = TransactionRecord(
|
||||||
|
uint32(0), uint32(0), False, False, now, spend_bundle, add_list, rem_list
|
||||||
|
)
|
||||||
|
await self.tx_store.add_transaction_record(tx_record)
|
||||||
|
|
||||||
|
async def remove_from_queue(self, spendbundle_id: bytes32):
|
||||||
|
"""
|
||||||
|
Full node received our transaction, no need to keep it in queue anymore
|
||||||
|
"""
|
||||||
|
await self.tx_store.set_sent(spendbundle_id)
|
||||||
|
|
||||||
|
async def get_send_queue(self) -> List[TransactionRecord]:
|
||||||
|
"""
|
||||||
|
Wallet Node uses this to retry sending transactions
|
||||||
|
"""
|
||||||
|
records = await self.tx_store.get_not_sent()
|
||||||
|
return records
|
||||||
|
|
||||||
|
async def get_all_transactions(self) -> List[TransactionRecord]:
|
||||||
|
"""
|
||||||
|
Retrieves all confirmed and pending transactions
|
||||||
|
"""
|
||||||
|
records = await self.tx_store.get_all_transactions()
|
||||||
|
return records
|
|
@ -2,8 +2,8 @@ import asyncio
|
||||||
from typing import Dict, Optional, List, Set
|
from typing import Dict, Optional, List, Set
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.CoinRecord import CoinRecord
|
from src.types.hashable.coin_record import CoinRecord
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.ints import uint32
|
from src.util.ints import uint32
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ class WalletStore:
|
||||||
# Whether or not we are syncing
|
# Whether or not we are syncing
|
||||||
sync_mode: bool = False
|
sync_mode: bool = False
|
||||||
lock: asyncio.Lock
|
lock: asyncio.Lock
|
||||||
lca_coin_records: Dict[str, CoinRecord]
|
coin_record_cache: Dict[str, CoinRecord]
|
||||||
cache_size: uint32
|
cache_size: uint32
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -61,7 +61,7 @@ class WalletStore:
|
||||||
await self.coin_record_db.commit()
|
await self.coin_record_db.commit()
|
||||||
# Lock
|
# Lock
|
||||||
self.lock = asyncio.Lock() # external
|
self.lock = asyncio.Lock() # external
|
||||||
self.lca_coin_records = dict()
|
self.coin_record_cache = dict()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
|
@ -89,11 +89,11 @@ class WalletStore:
|
||||||
)
|
)
|
||||||
await cursor.close()
|
await cursor.close()
|
||||||
await self.coin_record_db.commit()
|
await self.coin_record_db.commit()
|
||||||
self.lca_coin_records[record.coin.name().hex()] = record
|
self.coin_record_cache[record.coin.name().hex()] = record
|
||||||
if len(self.lca_coin_records) > self.cache_size:
|
if len(self.coin_record_cache) > self.cache_size:
|
||||||
while len(self.lca_coin_records) > self.cache_size:
|
while len(self.coin_record_cache) > self.cache_size:
|
||||||
first_in = list(self.lca_coin_records.keys())[0]
|
first_in = list(self.coin_record_cache.keys())[0]
|
||||||
del self.lca_coin_records[first_in]
|
del self.coin_record_cache[first_in]
|
||||||
|
|
||||||
# Update coin_record to be spent in DB
|
# Update coin_record to be spent in DB
|
||||||
async def set_spent(self, coin_name: bytes32, index: uint32):
|
async def set_spent(self, coin_name: bytes32, index: uint32):
|
||||||
|
@ -102,13 +102,13 @@ class WalletStore:
|
||||||
return
|
return
|
||||||
spent: CoinRecord = CoinRecord(
|
spent: CoinRecord = CoinRecord(
|
||||||
current.coin, current.confirmed_block_index, index, True, current.coinbase,
|
current.coin, current.confirmed_block_index, index, True, current.coinbase,
|
||||||
) # type: ignore # noqa
|
)
|
||||||
await self.add_coin_record(spent)
|
await self.add_coin_record(spent)
|
||||||
|
|
||||||
# Checks DB and DiffStores for CoinRecord with coin_name and returns it
|
# Checks DB and DiffStores for CoinRecord with coin_name and returns it
|
||||||
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]:
|
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]:
|
||||||
if coin_name.hex() in self.lca_coin_records:
|
if coin_name.hex() in self.coin_record_cache:
|
||||||
return self.lca_coin_records[coin_name.hex()]
|
return self.coin_record_cache[coin_name.hex()]
|
||||||
cursor = await self.coin_record_db.execute(
|
cursor = await self.coin_record_db.execute(
|
||||||
"SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)
|
"SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)
|
||||||
)
|
)
|
||||||
|
@ -157,7 +157,7 @@ class WalletStore:
|
||||||
async def rollback_lca_to_block(self, block_index):
|
async def rollback_lca_to_block(self, block_index):
|
||||||
# Update memory cache
|
# Update memory cache
|
||||||
delete_queue: bytes32 = []
|
delete_queue: bytes32 = []
|
||||||
for coin_name, coin_record in self.lca_coin_records.items():
|
for coin_name, coin_record in self.coin_record_cache.items():
|
||||||
if coin_record.spent_block_index > block_index:
|
if coin_record.spent_block_index > block_index:
|
||||||
new_record = CoinRecord(
|
new_record = CoinRecord(
|
||||||
coin_record.coin,
|
coin_record.coin,
|
||||||
|
@ -166,12 +166,12 @@ class WalletStore:
|
||||||
False,
|
False,
|
||||||
coin_record.coinbase,
|
coin_record.coinbase,
|
||||||
)
|
)
|
||||||
self.lca_coin_records[coin_record.coin.name().hex()] = new_record
|
self.coin_record_cache[coin_record.coin.name().hex()] = new_record
|
||||||
if coin_record.confirmed_block_index > block_index:
|
if coin_record.confirmed_block_index > block_index:
|
||||||
delete_queue.append(coin_name)
|
delete_queue.append(coin_name)
|
||||||
|
|
||||||
for coin_name in delete_queue:
|
for coin_name in delete_queue:
|
||||||
del self.lca_coin_records[coin_name]
|
del self.coin_record_cache[coin_name]
|
||||||
|
|
||||||
# Delete from storage
|
# Delete from storage
|
||||||
c1 = await self.coin_record_db.execute(
|
c1 = await self.coin_record_db.execute(
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
import asyncio
|
||||||
|
from typing import Dict, Optional, List
|
||||||
|
from pathlib import Path
|
||||||
|
import aiosqlite
|
||||||
|
from src.types.sized_bytes import bytes32
|
||||||
|
from src.util.ints import uint32
|
||||||
|
from src.wallet.transaction_record import TransactionRecord
|
||||||
|
|
||||||
|
|
||||||
|
class WalletTransactionStore:
|
||||||
|
"""
|
||||||
|
This object handles CoinRecords in DB used by wallet.
|
||||||
|
"""
|
||||||
|
|
||||||
|
transaction_db: aiosqlite.Connection
|
||||||
|
# Whether or not we are syncing
|
||||||
|
sync_mode: bool = False
|
||||||
|
lock: asyncio.Lock
|
||||||
|
cache_size: uint32
|
||||||
|
tx_record_cache: Dict[bytes32, TransactionRecord]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def create(cls, db_path: Path, cache_size: uint32 = uint32(600000)):
|
||||||
|
self = cls()
|
||||||
|
|
||||||
|
self.cache_size = cache_size
|
||||||
|
|
||||||
|
self.transaction_db = await aiosqlite.connect(db_path)
|
||||||
|
await self.transaction_db.execute(
|
||||||
|
(
|
||||||
|
f"CREATE TABLE IF NOT EXISTS transaction_record("
|
||||||
|
f"bundle_id text PRIMARY KEY,"
|
||||||
|
f" confirmed_index bigint,"
|
||||||
|
f" created_at_index bigint,"
|
||||||
|
f" confirmed int,"
|
||||||
|
f" sent int,"
|
||||||
|
f" created_at_time bigint,"
|
||||||
|
f" transaction_record blob)"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Useful for reorg lookups
|
||||||
|
await self.transaction_db.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS tx_confirmed_index on transaction_record(confirmed_index)"
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.transaction_db.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS tx_created_index on transaction_record(created_at_index)"
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.transaction_db.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS tx_confirmed on transaction_record(confirmed)"
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.transaction_db.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS tx_sent on transaction_record(sent)"
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.transaction_db.execute(
|
||||||
|
"CREATE INDEX IF NOT EXISTS tx_created_time on transaction_record(created_at_time)"
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.transaction_db.commit()
|
||||||
|
# Lock
|
||||||
|
self.lock = asyncio.Lock() # external
|
||||||
|
self.tx_record_cache = dict()
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
await self.transaction_db.close()
|
||||||
|
|
||||||
|
async def _init_cache(self):
|
||||||
|
print("init cache here")
|
||||||
|
|
||||||
|
async def _clear_database(self):
|
||||||
|
cursor = await self.transaction_db.execute("DELETE FROM transaction_record")
|
||||||
|
await cursor.close()
|
||||||
|
await self.transaction_db.commit()
|
||||||
|
|
||||||
|
# Store TransactionRecord in DB and Cache
|
||||||
|
async def add_transaction_record(self, record: TransactionRecord) -> None:
|
||||||
|
cursor = await self.transaction_db.execute(
|
||||||
|
"INSERT OR REPLACE INTO transaction_record VALUES(?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
(
|
||||||
|
record.name().hex(),
|
||||||
|
record.confirmed_block_index,
|
||||||
|
record.created_at_index,
|
||||||
|
int(record.confirmed),
|
||||||
|
int(record.sent),
|
||||||
|
record.created_at_time,
|
||||||
|
bytes(record),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
await cursor.close()
|
||||||
|
await self.transaction_db.commit()
|
||||||
|
self.tx_record_cache[record.name().hex()] = record
|
||||||
|
if len(self.tx_record_cache) > self.cache_size:
|
||||||
|
while len(self.tx_record_cache) > self.cache_size:
|
||||||
|
first_in = list(self.tx_record_cache.keys())[0]
|
||||||
|
self.tx_record_cache.pop(first_in)
|
||||||
|
|
||||||
|
# Update transaction_record to be confirmed in DB
|
||||||
|
async def set_confirmed(self, id: bytes32, index: uint32):
|
||||||
|
current: Optional[TransactionRecord] = await self.get_transaction_record(id)
|
||||||
|
if current is None:
|
||||||
|
return
|
||||||
|
tx: TransactionRecord = TransactionRecord(
|
||||||
|
index,
|
||||||
|
current.created_at_index,
|
||||||
|
True,
|
||||||
|
current.sent,
|
||||||
|
current.created_at_time,
|
||||||
|
current.spend_bundle,
|
||||||
|
current.additions,
|
||||||
|
current.removals,
|
||||||
|
)
|
||||||
|
await self.add_transaction_record(tx)
|
||||||
|
|
||||||
|
# Update transaction_record to be sent in DB
|
||||||
|
async def set_sent(self, id: bytes32):
|
||||||
|
current: Optional[TransactionRecord] = await self.get_transaction_record(id)
|
||||||
|
if current is None:
|
||||||
|
return
|
||||||
|
tx: TransactionRecord = TransactionRecord(
|
||||||
|
current.confirmed_block_index,
|
||||||
|
current.created_at_index,
|
||||||
|
current.confirmed,
|
||||||
|
True,
|
||||||
|
current.created_at_time,
|
||||||
|
current.spend_bundle,
|
||||||
|
current.additions,
|
||||||
|
current.removals,
|
||||||
|
)
|
||||||
|
await self.add_transaction_record(tx)
|
||||||
|
|
||||||
|
# Checks DB and cache for TransactionRecord with id: id and returns it
|
||||||
|
async def get_transaction_record(self, id: bytes32) -> Optional[TransactionRecord]:
|
||||||
|
if id.hex() in self.tx_record_cache:
|
||||||
|
return self.tx_record_cache[id.hex()]
|
||||||
|
cursor = await self.transaction_db.execute(
|
||||||
|
"SELECT * from transaction_record WHERE bundle_id=?", (id.hex(),)
|
||||||
|
)
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
await cursor.close()
|
||||||
|
if row is not None:
|
||||||
|
record = TransactionRecord.from_bytes(row[6])
|
||||||
|
return record
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_not_sent(self) -> List[TransactionRecord]:
|
||||||
|
cursor = await self.transaction_db.execute(
|
||||||
|
"SELECT * from transaction_record WHERE sent=?", (0,)
|
||||||
|
)
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
await cursor.close()
|
||||||
|
records = []
|
||||||
|
for row in rows:
|
||||||
|
record = TransactionRecord.from_bytes(row[6])
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
async def get_not_confirmed(self) -> List[TransactionRecord]:
|
||||||
|
cursor = await self.transaction_db.execute(
|
||||||
|
"SELECT * from transaction_record WHERE confirmed=?", (0,)
|
||||||
|
)
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
await cursor.close()
|
||||||
|
records = []
|
||||||
|
for row in rows:
|
||||||
|
record = TransactionRecord.from_bytes(row[6])
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
async def get_all_transactions(self) -> List[TransactionRecord]:
|
||||||
|
cursor = await self.transaction_db.execute("SELECT * from transaction_record")
|
||||||
|
rows = await cursor.fetchall()
|
||||||
|
await cursor.close()
|
||||||
|
records = []
|
||||||
|
for row in rows:
|
||||||
|
record = TransactionRecord.from_bytes(row[6])
|
||||||
|
records.append(record)
|
||||||
|
|
||||||
|
return records
|
|
@ -6,6 +6,7 @@ from pathlib import Path
|
||||||
|
|
||||||
import blspy
|
import blspy
|
||||||
from blspy import PrependSignature, PrivateKey, PublicKey
|
from blspy import PrependSignature, PrivateKey, PublicKey
|
||||||
|
from chiabip158 import PyBIP158
|
||||||
|
|
||||||
from chiapos import DiskPlotter, DiskProver
|
from chiapos import DiskPlotter, DiskProver
|
||||||
from lib.chiavdf.inkfish.classgroup import ClassGroup
|
from lib.chiavdf.inkfish.classgroup import ClassGroup
|
||||||
|
@ -18,19 +19,22 @@ from src.pool import create_coinbase_coin_and_signature
|
||||||
from src.types.body import Body
|
from src.types.body import Body
|
||||||
from src.types.challenge import Challenge
|
from src.types.challenge import Challenge
|
||||||
from src.types.classgroup import ClassgroupElement
|
from src.types.classgroup import ClassgroupElement
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock, additions_for_npc
|
||||||
from src.types.hashable.BLSSignature import BLSSignature
|
from src.types.hashable.BLSSignature import BLSSignature
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin, hash_coin_list
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.header import Header, HeaderData
|
from src.types.header import Header, HeaderData
|
||||||
from src.types.proof_of_space import ProofOfSpace
|
from src.types.proof_of_space import ProofOfSpace
|
||||||
from src.types.proof_of_time import ProofOfTime
|
from src.types.proof_of_time import ProofOfTime
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
|
from src.util.merkle_set import MerkleSet
|
||||||
from src.util.errors import NoProofsOfSpaceFound
|
from src.util.errors import NoProofsOfSpaceFound
|
||||||
from src.util.ints import uint8, uint32, uint64
|
from src.util.ints import uint8, uint32, uint64
|
||||||
from src.util.hash import std_hash
|
from src.util.hash import std_hash
|
||||||
|
|
||||||
# Can't go much lower than 19, since plots start having no solutions
|
# Can't go much lower than 19, since plots start having no solutions
|
||||||
|
from src.util.mempool_check_conditions import get_name_puzzle_conditions
|
||||||
|
|
||||||
k: uint8 = uint8(19)
|
k: uint8 = uint8(19)
|
||||||
# Uses many plots for testing, in order to guarantee proofs of space at every height
|
# Uses many plots for testing, in order to guarantee proofs of space at every height
|
||||||
num_plots = 40
|
num_plots = 40
|
||||||
|
@ -444,17 +448,63 @@ class BlockTools:
|
||||||
extension_data,
|
extension_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create filter
|
||||||
|
byte_array_tx: List[bytes32] = []
|
||||||
|
tx_additions: List[Coin] = []
|
||||||
|
tx_removals: List[bytes32] = []
|
||||||
|
if transactions:
|
||||||
|
error, npc_list, _ = get_name_puzzle_conditions(transactions)
|
||||||
|
additions: List[Coin] = additions_for_npc(npc_list)
|
||||||
|
for coin in additions:
|
||||||
|
tx_additions.append(coin)
|
||||||
|
byte_array_tx.append(bytearray(coin.puzzle_hash))
|
||||||
|
for npc in npc_list:
|
||||||
|
tx_removals.append(npc.coin_name)
|
||||||
|
byte_array_tx.append(bytearray(npc.coin_name))
|
||||||
|
|
||||||
|
byte_array_tx.append(bytearray(coinbase_coin.puzzle_hash))
|
||||||
|
byte_array_tx.append(bytearray(fees_coin.puzzle_hash))
|
||||||
|
|
||||||
|
bip158: PyBIP158 = PyBIP158(byte_array_tx)
|
||||||
|
encoded = bytes(bip158.GetEncoded())
|
||||||
|
|
||||||
|
removal_merkle_set = MerkleSet()
|
||||||
|
addition_merkle_set = MerkleSet()
|
||||||
|
|
||||||
|
tx_additions.append(coinbase_coin)
|
||||||
|
tx_additions.append(fees_coin)
|
||||||
|
|
||||||
|
# Create removal Merkle set
|
||||||
|
for coin_name in tx_removals:
|
||||||
|
removal_merkle_set.add_already_hashed(coin_name)
|
||||||
|
|
||||||
|
# Create addition Merkle set
|
||||||
|
puzzlehash_coin_map: Dict[bytes32, List[Coin]] = {}
|
||||||
|
for coin in tx_additions:
|
||||||
|
if coin.puzzle_hash in puzzlehash_coin_map:
|
||||||
|
puzzlehash_coin_map[coin.puzzle_hash].append(coin)
|
||||||
|
else:
|
||||||
|
puzzlehash_coin_map[coin.puzzle_hash] = [coin]
|
||||||
|
|
||||||
|
# Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash
|
||||||
|
for puzzle, coins in puzzlehash_coin_map.items():
|
||||||
|
addition_merkle_set.add_already_hashed(puzzle)
|
||||||
|
addition_merkle_set.add_already_hashed(hash_coin_list(coins))
|
||||||
|
|
||||||
|
additions_root = addition_merkle_set.get_root()
|
||||||
|
removal_root = removal_merkle_set.get_root()
|
||||||
|
|
||||||
header_data: HeaderData = HeaderData(
|
header_data: HeaderData = HeaderData(
|
||||||
height,
|
height,
|
||||||
prev_header_hash,
|
prev_header_hash,
|
||||||
timestamp,
|
timestamp,
|
||||||
bytes([0] * 32),
|
encoded,
|
||||||
proof_of_space.get_hash(),
|
proof_of_space.get_hash(),
|
||||||
body.get_hash(),
|
body.get_hash(),
|
||||||
uint64(prev_weight + difficulty),
|
uint64(prev_weight + difficulty),
|
||||||
uint64(prev_iters + number_iters),
|
uint64(prev_iters + number_iters),
|
||||||
bytes([0] * 32),
|
additions_root,
|
||||||
bytes([0] * 32),
|
removal_root,
|
||||||
)
|
)
|
||||||
|
|
||||||
header_hash_sig: PrependSignature = plot_sk.sign_prepend(header_data.get_hash())
|
header_hash_sig: PrependSignature = plot_sk.sign_prepend(header_data.get_hash())
|
||||||
|
|
|
@ -6,15 +6,14 @@ from pathlib import Path
|
||||||
import pytest
|
import pytest
|
||||||
from blspy import PrivateKey
|
from blspy import PrivateKey
|
||||||
|
|
||||||
from src.blockchain import Blockchain, ReceiveBlockResult
|
from src.full_node.blockchain import Blockchain, ReceiveBlockResult
|
||||||
from src.consensus.constants import constants
|
from src.full_node.store import FullNodeStore
|
||||||
from src.store import FullNodeStore
|
|
||||||
from src.types.body import Body
|
from src.types.body import Body
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.header import Header, HeaderData
|
from src.types.header import Header, HeaderData
|
||||||
from src.types.proof_of_space import ProofOfSpace
|
from src.types.proof_of_space import ProofOfSpace
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from src.util.ints import uint8, uint64
|
from src.util.ints import uint8, uint64
|
||||||
from tests.block_tools import BlockTools
|
from tests.block_tools import BlockTools
|
||||||
|
|
||||||
|
@ -29,6 +28,7 @@ test_constants: Dict[str, Any] = {
|
||||||
"DIFFICULTY_EPOCH": 12, # The number of blocks per epoch
|
"DIFFICULTY_EPOCH": 12, # The number of blocks per epoch
|
||||||
"DIFFICULTY_WARP_FACTOR": 4, # DELAY divides EPOCH in order to warp efficiently.
|
"DIFFICULTY_WARP_FACTOR": 4, # DELAY divides EPOCH in order to warp efficiently.
|
||||||
"DIFFICULTY_DELAY": 3, # EPOCH / WARP_FACTOR
|
"DIFFICULTY_DELAY": 3, # EPOCH / WARP_FACTOR
|
||||||
|
"VDF_IPS_STARTING": 50,
|
||||||
}
|
}
|
||||||
test_constants["GENESIS_BLOCK"] = bytes(
|
test_constants["GENESIS_BLOCK"] = bytes(
|
||||||
bt.create_genesis_block(test_constants, bytes([0] * 32), b"0")
|
bt.create_genesis_block(test_constants, bytes([0] * 32), b"0")
|
||||||
|
@ -47,7 +47,7 @@ class TestGenesisBlock:
|
||||||
unspent_store = await CoinStore.create(Path("blockchain_test.db"))
|
unspent_store = await CoinStore.create(Path("blockchain_test.db"))
|
||||||
store = await FullNodeStore.create(Path("blockchain_test.db"))
|
store = await FullNodeStore.create(Path("blockchain_test.db"))
|
||||||
await store._clear_database()
|
await store._clear_database()
|
||||||
bc1 = await Blockchain.create(unspent_store, store)
|
bc1 = await Blockchain.create(unspent_store, store, test_constants)
|
||||||
assert len(bc1.get_current_tips()) == 1
|
assert len(bc1.get_current_tips()) == 1
|
||||||
genesis_block = bc1.get_current_tips()[0]
|
genesis_block = bc1.get_current_tips()[0]
|
||||||
assert genesis_block.height == 0
|
assert genesis_block.height == 0
|
||||||
|
@ -90,7 +90,7 @@ class TestBlockValidation:
|
||||||
blocks[9].header.data.height,
|
blocks[9].header.data.height,
|
||||||
bytes([1] * 32),
|
bytes([1] * 32),
|
||||||
blocks[9].header.data.timestamp,
|
blocks[9].header.data.timestamp,
|
||||||
blocks[9].header.data.filter_hash,
|
blocks[9].header.data.filter,
|
||||||
blocks[9].header.data.proof_of_space_hash,
|
blocks[9].header.data.proof_of_space_hash,
|
||||||
blocks[9].header.data.body_hash,
|
blocks[9].header.data.body_hash,
|
||||||
blocks[9].header.data.weight,
|
blocks[9].header.data.weight,
|
||||||
|
@ -117,7 +117,7 @@ class TestBlockValidation:
|
||||||
blocks[9].header.data.height,
|
blocks[9].header.data.height,
|
||||||
blocks[9].header.data.prev_header_hash,
|
blocks[9].header.data.prev_header_hash,
|
||||||
blocks[9].header.data.timestamp - 1000,
|
blocks[9].header.data.timestamp - 1000,
|
||||||
blocks[9].header.data.filter_hash,
|
blocks[9].header.data.filter,
|
||||||
blocks[9].header.data.proof_of_space_hash,
|
blocks[9].header.data.proof_of_space_hash,
|
||||||
blocks[9].header.data.body_hash,
|
blocks[9].header.data.body_hash,
|
||||||
blocks[9].header.data.weight,
|
blocks[9].header.data.weight,
|
||||||
|
@ -141,7 +141,7 @@ class TestBlockValidation:
|
||||||
blocks[9].header.data.height,
|
blocks[9].header.data.height,
|
||||||
blocks[9].header.data.prev_header_hash,
|
blocks[9].header.data.prev_header_hash,
|
||||||
uint64(int(time.time() + 3600 * 3)),
|
uint64(int(time.time() + 3600 * 3)),
|
||||||
blocks[9].header.data.filter_hash,
|
blocks[9].header.data.filter,
|
||||||
blocks[9].header.data.proof_of_space_hash,
|
blocks[9].header.data.proof_of_space_hash,
|
||||||
blocks[9].header.data.body_hash,
|
blocks[9].header.data.body_hash,
|
||||||
blocks[9].header.data.weight,
|
blocks[9].header.data.weight,
|
||||||
|
@ -167,7 +167,7 @@ class TestBlockValidation:
|
||||||
blocks[9].header.data.height,
|
blocks[9].header.data.height,
|
||||||
blocks[9].header.data.prev_header_hash,
|
blocks[9].header.data.prev_header_hash,
|
||||||
blocks[9].header.data.timestamp,
|
blocks[9].header.data.timestamp,
|
||||||
blocks[9].header.data.filter_hash,
|
blocks[9].header.data.filter,
|
||||||
blocks[9].header.data.proof_of_space_hash,
|
blocks[9].header.data.proof_of_space_hash,
|
||||||
bytes([1] * 32),
|
bytes([1] * 32),
|
||||||
blocks[9].header.data.weight,
|
blocks[9].header.data.weight,
|
||||||
|
@ -268,7 +268,7 @@ class TestBlockValidation:
|
||||||
assert diff_27 > diff_26
|
assert diff_27 > diff_26
|
||||||
assert (diff_27 / diff_26) <= test_constants["DIFFICULTY_FACTOR"]
|
assert (diff_27 / diff_26) <= test_constants["DIFFICULTY_FACTOR"]
|
||||||
|
|
||||||
assert (b.get_next_ips(blocks[1])) == constants["VDF_IPS_STARTING"]
|
assert (b.get_next_ips(blocks[1])) == test_constants["VDF_IPS_STARTING"]
|
||||||
assert (b.get_next_ips(blocks[24])) == (b.get_next_ips(blocks[23]))
|
assert (b.get_next_ips(blocks[24])) == (b.get_next_ips(blocks[23]))
|
||||||
assert (b.get_next_ips(blocks[25])) == (b.get_next_ips(blocks[24]))
|
assert (b.get_next_ips(blocks[25])) == (b.get_next_ips(blocks[24]))
|
||||||
assert (b.get_next_ips(blocks[26])) > (b.get_next_ips(blocks[25]))
|
assert (b.get_next_ips(blocks[26])) > (b.get_next_ips(blocks[25]))
|
||||||
|
@ -292,7 +292,7 @@ class TestReorgs:
|
||||||
assert b.get_current_tips()[0].height == 100
|
assert b.get_current_tips()[0].height == 100
|
||||||
|
|
||||||
blocks_reorg_chain = bt.get_consecutive_blocks(
|
blocks_reorg_chain = bt.get_consecutive_blocks(
|
||||||
test_constants, 30, blocks[:90], 9, b"1"
|
test_constants, 30, blocks[:90], 9, b"2"
|
||||||
)
|
)
|
||||||
for i in range(1, len(blocks_reorg_chain)):
|
for i in range(1, len(blocks_reorg_chain)):
|
||||||
reorg_block = blocks_reorg_chain[i]
|
reorg_block = blocks_reorg_chain[i]
|
||||||
|
@ -321,9 +321,10 @@ class TestReorgs:
|
||||||
|
|
||||||
# Reorg from genesis
|
# Reorg from genesis
|
||||||
blocks_reorg_chain = bt.get_consecutive_blocks(
|
blocks_reorg_chain = bt.get_consecutive_blocks(
|
||||||
test_constants, 21, [blocks[0]], 9, b"1"
|
test_constants, 21, [blocks[0]], 9, b"3"
|
||||||
)
|
)
|
||||||
for i in range(1, len(blocks_reorg_chain)):
|
for i in range(1, len(blocks_reorg_chain)):
|
||||||
|
print("I", i)
|
||||||
reorg_block = blocks_reorg_chain[i]
|
reorg_block = blocks_reorg_chain[i]
|
||||||
result, removed = await b.receive_block(reorg_block)
|
result, removed = await b.receive_block(reorg_block)
|
||||||
if reorg_block.height == 0:
|
if reorg_block.height == 0:
|
||||||
|
@ -336,7 +337,7 @@ class TestReorgs:
|
||||||
|
|
||||||
# Reorg back to original branch
|
# Reorg back to original branch
|
||||||
blocks_reorg_chain_2 = bt.get_consecutive_blocks(
|
blocks_reorg_chain_2 = bt.get_consecutive_blocks(
|
||||||
test_constants, 3, blocks[:-1], 9, b"3"
|
test_constants, 3, blocks[:-1], 9, b"4"
|
||||||
)
|
)
|
||||||
result, _ = await b.receive_block(blocks_reorg_chain_2[20])
|
result, _ = await b.receive_block(blocks_reorg_chain_2[20])
|
||||||
assert result == ReceiveBlockResult.ADDED_AS_ORPHAN
|
assert result == ReceiveBlockResult.ADDED_AS_ORPHAN
|
||||||
|
@ -389,8 +390,6 @@ class TestReorgs:
|
||||||
await b.receive_block(blocks[i])
|
await b.receive_block(blocks[i])
|
||||||
header_hashes = b.get_header_hashes(blocks[-1].header_hash)
|
header_hashes = b.get_header_hashes(blocks[-1].header_hash)
|
||||||
assert len(header_hashes) == 6
|
assert len(header_hashes) == 6
|
||||||
print(header_hashes)
|
|
||||||
print([block.header_hash for block in blocks])
|
|
||||||
assert header_hashes == [block.header_hash for block in blocks]
|
assert header_hashes == [block.header_hash for block in blocks]
|
||||||
|
|
||||||
await unspent_store.close()
|
await unspent_store.close()
|
|
@ -4,13 +4,13 @@ from typing import Optional
|
||||||
import pytest
|
import pytest
|
||||||
from clvm.casts import int_to_bytes
|
from clvm.casts import int_to_bytes
|
||||||
|
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from src.util.bundle_tools import best_solution_program
|
from src.util.bundle_tools import best_solution_program
|
||||||
from src.server.outbound_message import OutboundMessage
|
from src.server.outbound_message import OutboundMessage
|
||||||
from src.protocols import full_node_protocol
|
from src.protocols import full_node_protocol
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.util.ConsensusError import Err
|
from src.util.ConsensusError import Err
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64
|
||||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
|
@ -11,10 +11,10 @@ from src.protocols import timelord_protocol
|
||||||
from src.types.peer_info import PeerInfo
|
from src.types.peer_info import PeerInfo
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.proof_of_space import ProofOfSpace
|
from src.types.proof_of_space import ProofOfSpace
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.util.bundle_tools import best_solution_program
|
from src.util.bundle_tools import best_solution_program
|
||||||
from src.util.ints import uint16, uint32, uint64, uint8
|
from src.util.ints import uint16, uint32, uint64, uint8
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||||
from tests.wallet_tools import WalletTool
|
from tests.wallet_tools import WalletTool
|
|
@ -5,7 +5,7 @@ import pytest
|
||||||
|
|
||||||
from src.server.outbound_message import OutboundMessage
|
from src.server.outbound_message import OutboundMessage
|
||||||
from src.protocols import full_node_protocol
|
from src.protocols import full_node_protocol
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from src.util.ints import uint64
|
from src.util.ints import uint64
|
||||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||||
|
@ -43,7 +43,6 @@ class TestMempool:
|
||||||
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
||||||
|
|
||||||
block = blocks[1]
|
block = blocks[1]
|
||||||
print(f"block coinbase: {block.body.coinbase.name()}")
|
|
||||||
async for _ in full_node_1.respond_block(
|
async for _ in full_node_1.respond_block(
|
||||||
full_node_protocol.RespondBlock(block)
|
full_node_protocol.RespondBlock(block)
|
||||||
):
|
):
|
||||||
|
@ -113,7 +112,6 @@ class TestMempool:
|
||||||
outbound_2: OutboundMessage = _
|
outbound_2: OutboundMessage = _
|
||||||
# Maybe transaction means that it's accepted in mempool
|
# Maybe transaction means that it's accepted in mempool
|
||||||
assert outbound_2.message.function == "new_transaction"
|
assert outbound_2.message.function == "new_transaction"
|
||||||
print(blocks[1].body.coinbase.name())
|
|
||||||
sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
|
sb = full_node_1.mempool_manager.get_spendbundle(spend_bundle.name())
|
||||||
assert sb is spend_bundle
|
assert sb is spend_bundle
|
||||||
|
|
|
@ -6,8 +6,7 @@ import sqlite3
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from src.consensus.constants import constants
|
from src.full_node.store import FullNodeStore
|
||||||
from src.store import FullNodeStore
|
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.types.sized_bytes import bytes32
|
from src.types.sized_bytes import bytes32
|
||||||
from src.util.ints import uint32, uint64
|
from src.util.ints import uint32, uint64
|
||||||
|
@ -58,7 +57,7 @@ class TestStore:
|
||||||
try:
|
try:
|
||||||
await db._clear_database()
|
await db._clear_database()
|
||||||
|
|
||||||
genesis = FullBlock.from_bytes(constants["GENESIS_BLOCK"])
|
genesis = FullBlock.from_bytes(test_constants["GENESIS_BLOCK"])
|
||||||
|
|
||||||
# Save/get block
|
# Save/get block
|
||||||
for block in blocks:
|
for block in blocks:
|
|
@ -4,9 +4,9 @@ from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from src.blockchain import Blockchain, ReceiveBlockResult
|
from src.full_node.blockchain import Blockchain, ReceiveBlockResult
|
||||||
from src.store import FullNodeStore
|
from src.full_node.store import FullNodeStore
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from tests.block_tools import BlockTools
|
from tests.block_tools import BlockTools
|
||||||
|
|
||||||
bt = BlockTools()
|
bt = BlockTools()
|
|
@ -1,7 +1,7 @@
|
||||||
import blspy
|
import blspy
|
||||||
|
|
||||||
from src.types.hashable.CoinSolution import CoinSolution
|
from src.types.hashable.coin_solution import CoinSolution
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
|
|
||||||
from src.wallet.BLSPrivateKey import BLSPrivateKey
|
from src.wallet.BLSPrivateKey import BLSPrivateKey
|
||||||
from src.wallet.keychain import Keychain
|
from src.wallet.keychain import Keychain
|
||||||
|
|
|
@ -1,37 +1,11 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any, Dict
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from src.blockchain import Blockchain, ReceiveBlockResult
|
|
||||||
from src.mempool_manager import MempoolManager
|
|
||||||
from src.store import FullNodeStore
|
|
||||||
from src.full_node import FullNode
|
|
||||||
from src.server.connection import NodeType
|
|
||||||
from src.server.server import ChiaServer
|
|
||||||
from src.coin_store import CoinStore
|
|
||||||
from tests.block_tools import BlockTools
|
|
||||||
from src.rpc.rpc_server import start_rpc_server
|
from src.rpc.rpc_server import start_rpc_server
|
||||||
|
from src.protocols import full_node_protocol
|
||||||
from src.rpc.rpc_client import RpcClient
|
from src.rpc.rpc_client import RpcClient
|
||||||
from src.util.config import load_config
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||||
|
|
||||||
|
|
||||||
bt = BlockTools()
|
|
||||||
|
|
||||||
test_constants: Dict[str, Any] = {
|
|
||||||
"DIFFICULTY_STARTING": 5,
|
|
||||||
"DISCRIMINANT_SIZE_BITS": 32,
|
|
||||||
"BLOCK_TIME_TARGET": 10,
|
|
||||||
"MIN_BLOCK_TIME": 2,
|
|
||||||
"DIFFICULTY_FACTOR": 3,
|
|
||||||
"DIFFICULTY_EPOCH": 12, # The number of blocks per epoch
|
|
||||||
"DIFFICULTY_WARP_FACTOR": 4, # DELAY divides EPOCH in order to warp efficiently.
|
|
||||||
"DIFFICULTY_DELAY": 3, # EPOCH / WARP_FACTOR
|
|
||||||
}
|
|
||||||
test_constants["GENESIS_BLOCK"] = bytes(
|
|
||||||
bt.create_genesis_block(test_constants, bytes([0] * 32), b"0")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
|
@ -41,47 +15,28 @@ def event_loop():
|
||||||
|
|
||||||
|
|
||||||
class TestRpc:
|
class TestRpc:
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
async def two_nodes(self):
|
||||||
|
async for _ in setup_two_nodes():
|
||||||
|
yield _
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test1(self):
|
async def test1(self, two_nodes):
|
||||||
test_node_1_port = 21234
|
num_blocks = 10
|
||||||
test_node_2_port = 21235
|
test_rpc_port = 21522
|
||||||
test_rpc_port = 21236
|
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
||||||
db_filename = Path("blockchain_test.db")
|
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
|
||||||
|
|
||||||
if db_filename.exists():
|
for i in range(1, num_blocks):
|
||||||
db_filename.unlink()
|
async for _ in full_node_1.respond_block(
|
||||||
store = await FullNodeStore.create(db_filename)
|
full_node_protocol.RespondBlock(blocks[i])
|
||||||
await store._clear_database()
|
):
|
||||||
blocks = bt.get_consecutive_blocks(test_constants, 10, [], 10)
|
pass
|
||||||
unspent_store = await CoinStore.create("blockchain_test.db")
|
|
||||||
mempool_manager = MempoolManager(unspent_store)
|
|
||||||
|
|
||||||
b: Blockchain = await Blockchain.create(unspent_store, store, test_constants)
|
|
||||||
await store.add_block(blocks[0])
|
|
||||||
for i in range(1, len(blocks)):
|
|
||||||
assert (await b.receive_block(blocks[i]))[
|
|
||||||
0
|
|
||||||
] == ReceiveBlockResult.ADDED_TO_HEAD
|
|
||||||
await store.add_block(blocks[i])
|
|
||||||
|
|
||||||
config = load_config("config.yaml", "full_node")
|
|
||||||
full_node_1 = FullNode(store, b, config, mempool_manager, unspent_store)
|
|
||||||
server_1 = ChiaServer(test_node_1_port, full_node_1, NodeType.FULL_NODE)
|
|
||||||
_ = await server_1.start_server("127.0.0.1", None)
|
|
||||||
full_node_1._set_server(server_1)
|
|
||||||
|
|
||||||
def stop_node_cb():
|
def stop_node_cb():
|
||||||
full_node_1._shutdown()
|
full_node_1._shutdown()
|
||||||
server_1.close_all()
|
server_1.close_all()
|
||||||
|
|
||||||
unspent_store2 = await CoinStore.create("blockchain_test_2.db")
|
|
||||||
mempool_manager2 = MempoolManager(unspent_store2)
|
|
||||||
full_node_2 = FullNode(store, b, config, mempool_manager2, unspent_store2)
|
|
||||||
server_2 = ChiaServer(test_node_2_port, full_node_2, NodeType.FULL_NODE)
|
|
||||||
full_node_2._set_server(server_2)
|
|
||||||
|
|
||||||
_ = await server_2.start_server("127.0.0.1", None)
|
|
||||||
|
|
||||||
rpc_cleanup = await start_rpc_server(full_node_1, stop_node_cb, test_rpc_port)
|
rpc_cleanup = await start_rpc_server(full_node_1, stop_node_cb, test_rpc_port)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -94,22 +49,20 @@ class TestRpc:
|
||||||
assert state["ips"] > 0
|
assert state["ips"] > 0
|
||||||
|
|
||||||
block = await client.get_block(state["lca"].header_hash)
|
block = await client.get_block(state["lca"].header_hash)
|
||||||
print([b.header_hash for b in blocks])
|
assert block == blocks[7]
|
||||||
print(block.header_hash)
|
|
||||||
assert block == blocks[8]
|
|
||||||
assert (await client.get_block(bytes([1] * 32))) is None
|
assert (await client.get_block(bytes([1] * 32))) is None
|
||||||
|
|
||||||
header = await client.get_header(state["lca"].header_hash)
|
header = await client.get_header(state["lca"].header_hash)
|
||||||
assert header == blocks[8].header
|
assert header == blocks[7].header
|
||||||
|
|
||||||
coins = await client.get_unspent_coins(
|
coins = await client.get_unspent_coins(
|
||||||
blocks[-1].body.coinbase.puzzle_hash, blocks[-1].header_hash
|
blocks[-1].body.coinbase.puzzle_hash, blocks[-1].header_hash
|
||||||
)
|
)
|
||||||
assert len(coins) == 22
|
assert len(coins) == 16
|
||||||
coins_lca = await client.get_unspent_coins(
|
coins_lca = await client.get_unspent_coins(
|
||||||
blocks[-1].body.coinbase.puzzle_hash
|
blocks[-1].body.coinbase.puzzle_hash
|
||||||
)
|
)
|
||||||
assert len(coins_lca) == 18
|
assert len(coins_lca) == 16
|
||||||
|
|
||||||
assert len(await client.get_connections()) == 0
|
assert len(await client.get_connections()) == 0
|
||||||
|
|
||||||
|
@ -123,27 +76,11 @@ class TestRpc:
|
||||||
await asyncio.sleep(2) # Allow server to start
|
await asyncio.sleep(2) # Allow server to start
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
# Checks that the RPC manages to stop the node
|
# Checks that the RPC manages to stop the node
|
||||||
await client.stop_node()
|
|
||||||
client.close()
|
client.close()
|
||||||
await client.await_closed()
|
await client.await_closed()
|
||||||
server_2.close_all()
|
|
||||||
await server_1.await_closed()
|
|
||||||
await server_2.await_closed()
|
|
||||||
await rpc_cleanup()
|
await rpc_cleanup()
|
||||||
await store.close()
|
|
||||||
Path("blockchain_test.db").unlink()
|
|
||||||
Path("blockchain_test_2.db").unlink()
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
await client.stop_node()
|
|
||||||
client.close()
|
client.close()
|
||||||
await client.await_closed()
|
await client.await_closed()
|
||||||
server_2.close_all()
|
|
||||||
await server_1.await_closed()
|
|
||||||
await server_2.await_closed()
|
|
||||||
await rpc_cleanup()
|
await rpc_cleanup()
|
||||||
await store.close()
|
|
||||||
await unspent_store.close()
|
|
||||||
await unspent_store2.close()
|
|
||||||
Path("blockchain_test.db").unlink()
|
|
||||||
Path("blockchain_test_2.db").unlink()
|
|
||||||
|
|
|
@ -2,14 +2,14 @@ from typing import Any, Dict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from src.blockchain import Blockchain
|
from src.full_node.blockchain import Blockchain
|
||||||
from src.mempool_manager import MempoolManager
|
from src.full_node.mempool_manager import MempoolManager
|
||||||
from src.store import FullNodeStore
|
from src.full_node.store import FullNodeStore
|
||||||
from src.full_node import FullNode
|
from src.full_node.full_node import FullNode
|
||||||
from src.server.connection import NodeType
|
from src.server.connection import NodeType
|
||||||
from src.server.server import ChiaServer
|
from src.server.server import ChiaServer
|
||||||
from src.types.full_block import FullBlock
|
from src.types.full_block import FullBlock
|
||||||
from src.coin_store import CoinStore
|
from src.full_node.coin_store import CoinStore
|
||||||
from tests.block_tools import BlockTools
|
from tests.block_tools import BlockTools
|
||||||
from src.types.hashable.BLSSignature import BLSPublicKey
|
from src.types.hashable.BLSSignature import BLSPublicKey
|
||||||
from src.util.config import load_config
|
from src.util.config import load_config
|
||||||
|
@ -68,7 +68,13 @@ async def setup_full_node(db_name, port, introducer_port=None, dic={}):
|
||||||
config["introducer_peer"]["host"] = "127.0.0.1"
|
config["introducer_peer"]["host"] = "127.0.0.1"
|
||||||
config["introducer_peer"]["port"] = introducer_port
|
config["introducer_peer"]["port"] = introducer_port
|
||||||
full_node_1 = FullNode(
|
full_node_1 = FullNode(
|
||||||
store_1, b_1, config, mempool_1, unspent_store_1, f"full_node_{port}"
|
store_1,
|
||||||
|
b_1,
|
||||||
|
config,
|
||||||
|
mempool_1,
|
||||||
|
unspent_store_1,
|
||||||
|
f"full_node_{port}",
|
||||||
|
test_constants_copy,
|
||||||
)
|
)
|
||||||
server_1 = ChiaServer(port, full_node_1, NodeType.FULL_NODE)
|
server_1 = ChiaServer(port, full_node_1, NodeType.FULL_NODE)
|
||||||
_ = await server_1.start_server(config["host"], full_node_1._on_connect)
|
_ = await server_1.start_server(config["host"], full_node_1._on_connect)
|
||||||
|
@ -117,8 +123,11 @@ async def setup_farmer(port, dic={}):
|
||||||
"pool_sks": [bytes(pool_sk).hex()],
|
"pool_sks": [bytes(pool_sk).hex()],
|
||||||
"pool_target": pool_target.hex(),
|
"pool_target": pool_target.hex(),
|
||||||
}
|
}
|
||||||
|
test_constants_copy = test_constants.copy()
|
||||||
|
for k in dic.keys():
|
||||||
|
test_constants_copy[k] = dic[k]
|
||||||
|
|
||||||
farmer = Farmer(config, key_config)
|
farmer = Farmer(config, key_config, test_constants_copy)
|
||||||
server = ChiaServer(port, farmer, NodeType.FARMER)
|
server = ChiaServer(port, farmer, NodeType.FARMER)
|
||||||
_ = await server.start_server(config["host"], farmer._on_connect)
|
_ = await server.start_server(config["host"], farmer._on_connect)
|
||||||
|
|
||||||
|
@ -143,8 +152,11 @@ async def setup_introducer(port, dic={}):
|
||||||
|
|
||||||
async def setup_timelord(port, dic={}):
|
async def setup_timelord(port, dic={}):
|
||||||
config = load_config("config.yaml", "timelord")
|
config = load_config("config.yaml", "timelord")
|
||||||
|
test_constants_copy = test_constants.copy()
|
||||||
|
for k in dic.keys():
|
||||||
|
test_constants_copy[k] = dic[k]
|
||||||
|
|
||||||
timelord = Timelord(config)
|
timelord = Timelord(config, test_constants_copy["DISCRIMINANT_SIZE_BITS"])
|
||||||
server = ChiaServer(port, timelord, NodeType.TIMELORD)
|
server = ChiaServer(port, timelord, NodeType.TIMELORD)
|
||||||
_ = await server.start_server(port, None)
|
_ = await server.start_server(port, None)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import asyncio
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from blspy import ExtendedPrivateKey
|
||||||
|
from chiabip158 import PyBIP158
|
||||||
|
|
||||||
|
from src.wallet.wallet_node import WalletNode
|
||||||
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def event_loop():
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
yield loop
|
||||||
|
|
||||||
|
|
||||||
|
class TestFilter:
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
async def two_nodes(self):
|
||||||
|
async for _ in setup_two_nodes({"COINBASE_FREEZE_PERIOD": 0}):
|
||||||
|
yield _
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_basic_filter_test(self, two_nodes):
|
||||||
|
sk = bytes(ExtendedPrivateKey.from_seed(b"")).hex()
|
||||||
|
key_config = {"wallet_sk": sk}
|
||||||
|
wallet_node = await WalletNode.create({}, key_config)
|
||||||
|
wallet = wallet_node.wallet
|
||||||
|
await wallet_node.wallet_store._clear_database()
|
||||||
|
|
||||||
|
num_blocks = 2
|
||||||
|
blocks = bt.get_consecutive_blocks(
|
||||||
|
test_constants,
|
||||||
|
num_blocks,
|
||||||
|
[],
|
||||||
|
10,
|
||||||
|
reward_puzzlehash=wallet.get_new_puzzlehash(),
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in range(1, num_blocks):
|
||||||
|
byte_array_tx: List[bytes] = []
|
||||||
|
block = blocks[i]
|
||||||
|
coinbase = bytearray(block.body.coinbase.puzzle_hash)
|
||||||
|
fee = bytearray(block.body.fees_coin.puzzle_hash)
|
||||||
|
byte_array_tx.append(coinbase)
|
||||||
|
byte_array_tx.append(fee)
|
||||||
|
|
||||||
|
pl = PyBIP158(byte_array_tx)
|
||||||
|
present = pl.Match(coinbase)
|
||||||
|
fee_present = pl.Match(fee)
|
||||||
|
|
||||||
|
assert present
|
||||||
|
assert fee_present
|
||||||
|
|
||||||
|
await wallet_node.wallet_store.close()
|
||||||
|
await wallet_node.tx_store.close()
|
|
@ -0,0 +1,50 @@
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.util.merkle_set import MerkleSet, confirm_included_already_hashed
|
||||||
|
from tests.setup_nodes import test_constants, bt
|
||||||
|
from tests.wallet_tools import WalletTool
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def event_loop():
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
yield loop
|
||||||
|
|
||||||
|
|
||||||
|
class TestMerkleSet:
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_basics(self):
|
||||||
|
wallet_tool = WalletTool()
|
||||||
|
|
||||||
|
num_blocks = 10
|
||||||
|
blocks = bt.get_consecutive_blocks(
|
||||||
|
test_constants,
|
||||||
|
num_blocks,
|
||||||
|
[],
|
||||||
|
10,
|
||||||
|
reward_puzzlehash=wallet_tool.get_new_puzzlehash(),
|
||||||
|
)
|
||||||
|
|
||||||
|
merkle_set = MerkleSet()
|
||||||
|
for block in blocks:
|
||||||
|
merkle_set.add_already_hashed(block.body.coinbase.name())
|
||||||
|
|
||||||
|
for block in blocks:
|
||||||
|
result, proof = merkle_set.is_included_already_hashed(
|
||||||
|
block.body.coinbase.name()
|
||||||
|
)
|
||||||
|
assert result is True
|
||||||
|
result_fee, proof_fee = merkle_set.is_included_already_hashed(
|
||||||
|
block.body.fees_coin.name()
|
||||||
|
)
|
||||||
|
assert result_fee is False
|
||||||
|
validate_proof = confirm_included_already_hashed(
|
||||||
|
merkle_set.get_root(), block.body.coinbase.name(), proof
|
||||||
|
)
|
||||||
|
validate_proof_fee = confirm_included_already_hashed(
|
||||||
|
merkle_set.get_root(), block.body.fees_coin.name(), proof_fee
|
||||||
|
)
|
||||||
|
assert validate_proof is True
|
||||||
|
assert validate_proof_fee is False
|
|
@ -5,6 +5,7 @@ from blspy import ExtendedPrivateKey
|
||||||
|
|
||||||
from src.protocols.wallet_protocol import RespondBody
|
from src.protocols.wallet_protocol import RespondBody
|
||||||
from src.wallet.wallet import Wallet
|
from src.wallet.wallet import Wallet
|
||||||
|
from src.wallet.wallet_node import WalletNode
|
||||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,8 +26,10 @@ class TestWallet:
|
||||||
sk = bytes(ExtendedPrivateKey.from_seed(b"")).hex()
|
sk = bytes(ExtendedPrivateKey.from_seed(b"")).hex()
|
||||||
key_config = {"wallet_sk": sk}
|
key_config = {"wallet_sk": sk}
|
||||||
|
|
||||||
wallet = await Wallet.create({}, key_config)
|
wallet_node = await WalletNode.create({}, key_config)
|
||||||
await wallet.wallet_store._clear_database()
|
wallet = wallet_node.wallet
|
||||||
|
await wallet_node.wallet_store._clear_database()
|
||||||
|
await wallet_node.tx_store._clear_database()
|
||||||
|
|
||||||
num_blocks = 10
|
num_blocks = 10
|
||||||
blocks = bt.get_consecutive_blocks(
|
blocks = bt.get_consecutive_blocks(
|
||||||
|
@ -39,11 +42,12 @@ class TestWallet:
|
||||||
|
|
||||||
for i in range(1, num_blocks):
|
for i in range(1, num_blocks):
|
||||||
a = RespondBody(blocks[i].body, blocks[i].height)
|
a = RespondBody(blocks[i].body, blocks[i].height)
|
||||||
await wallet.received_body(a)
|
await wallet_node.received_body(a)
|
||||||
|
|
||||||
assert await wallet.get_confirmed_balance() == 144000000000000
|
assert await wallet.get_confirmed_balance() == 144000000000000
|
||||||
|
|
||||||
await wallet.wallet_store.close()
|
await wallet_node.wallet_store.close()
|
||||||
|
await wallet_node.tx_store.close()
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_wallet_make_transaction(self, two_nodes):
|
async def test_wallet_make_transaction(self, two_nodes):
|
||||||
|
@ -52,10 +56,15 @@ class TestWallet:
|
||||||
key_config = {"wallet_sk": sk}
|
key_config = {"wallet_sk": sk}
|
||||||
key_config_b = {"wallet_sk": sk_b}
|
key_config_b = {"wallet_sk": sk_b}
|
||||||
|
|
||||||
wallet = await Wallet.create({}, key_config)
|
wallet_node = await WalletNode.create({}, key_config)
|
||||||
await wallet.wallet_store._clear_database()
|
wallet = wallet_node.wallet
|
||||||
wallet_b = await Wallet.create({}, key_config_b)
|
await wallet_node.wallet_store._clear_database()
|
||||||
await wallet_b.wallet_store._clear_database()
|
await wallet_node.tx_store._clear_database()
|
||||||
|
|
||||||
|
wallet_node_b = await WalletNode.create({}, key_config_b)
|
||||||
|
wallet_b = wallet_node_b.wallet
|
||||||
|
await wallet_node_b.wallet_store._clear_database()
|
||||||
|
await wallet_node_b.tx_store._clear_database()
|
||||||
|
|
||||||
num_blocks = 10
|
num_blocks = 10
|
||||||
blocks = bt.get_consecutive_blocks(
|
blocks = bt.get_consecutive_blocks(
|
||||||
|
@ -68,7 +77,7 @@ class TestWallet:
|
||||||
|
|
||||||
for i in range(1, num_blocks):
|
for i in range(1, num_blocks):
|
||||||
a = RespondBody(blocks[i].body, blocks[i].height)
|
a = RespondBody(blocks[i].body, blocks[i].height)
|
||||||
await wallet.received_body(a)
|
await wallet_node.received_body(a)
|
||||||
|
|
||||||
assert await wallet.get_confirmed_balance() == 144000000000000
|
assert await wallet.get_confirmed_balance() == 144000000000000
|
||||||
|
|
||||||
|
@ -83,5 +92,8 @@ class TestWallet:
|
||||||
assert confirmed_balance == 144000000000000
|
assert confirmed_balance == 144000000000000
|
||||||
assert unconfirmed_balance == confirmed_balance - 10
|
assert unconfirmed_balance == confirmed_balance - 10
|
||||||
|
|
||||||
await wallet.wallet_store.close()
|
await wallet_node.wallet_store.close()
|
||||||
await wallet_b.wallet_store.close()
|
await wallet_node.tx_store.close()
|
||||||
|
|
||||||
|
await wallet_node_b.wallet_store.close()
|
||||||
|
await wallet_node_b.tx_store.close()
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import asyncio
|
|
||||||
import signal
|
|
||||||
import time
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from blspy import ExtendedPrivateKey
|
|
||||||
|
|
||||||
from src.protocols import full_node_protocol
|
|
||||||
from src.server.outbound_message import NodeType
|
|
||||||
from src.server.server import ChiaServer
|
|
||||||
from src.types.peer_info import PeerInfo
|
|
||||||
from src.wallet.wallet import Wallet
|
|
||||||
from tests.setup_nodes import setup_two_nodes, test_constants, bt
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
|
||||||
def event_loop():
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
yield loop
|
|
||||||
|
|
||||||
|
|
||||||
class TestWalletProtocol:
|
|
||||||
@pytest.fixture(scope="function")
|
|
||||||
async def two_nodes(self):
|
|
||||||
async for _ in setup_two_nodes({"COINBASE_FREEZE_PERIOD": 0}):
|
|
||||||
yield _
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_wallet_connect(self, two_nodes):
|
|
||||||
num_blocks = 10
|
|
||||||
blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
|
|
||||||
full_node_1, full_node_2, server_1, server_2 = two_nodes
|
|
||||||
|
|
||||||
for i in range(1, num_blocks):
|
|
||||||
async for _ in full_node_1.respond_block(
|
|
||||||
full_node_protocol.RespondBlock(blocks[i])
|
|
||||||
):
|
|
||||||
pass
|
|
||||||
|
|
||||||
sk = bytes(ExtendedPrivateKey.from_seed(b"")).hex()
|
|
||||||
key_config = {"wallet_sk": sk}
|
|
||||||
|
|
||||||
wallet = await Wallet.create({}, key_config)
|
|
||||||
server = ChiaServer(8223, wallet, NodeType.WALLET)
|
|
||||||
|
|
||||||
asyncio.get_running_loop().add_signal_handler(signal.SIGINT, server.close_all)
|
|
||||||
asyncio.get_running_loop().add_signal_handler(signal.SIGTERM, server.close_all)
|
|
||||||
|
|
||||||
_ = await server.start_server("127.0.0.1", wallet._on_connect)
|
|
||||||
await asyncio.sleep(2)
|
|
||||||
full_node_peer = PeerInfo(server_1._host, server_1._port)
|
|
||||||
_ = await server.start_client(full_node_peer, None)
|
|
||||||
|
|
||||||
start_unf = time.time()
|
|
||||||
while time.time() - start_unf < 3:
|
|
||||||
# TODO check if we've synced proof hashes and verified number of proofs
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
|
|
||||||
await wallet.wallet_store.close()
|
|
|
@ -5,13 +5,13 @@ from clvm.casts import int_to_bytes, int_from_bytes
|
||||||
from os import urandom
|
from os import urandom
|
||||||
from blspy import ExtendedPrivateKey
|
from blspy import ExtendedPrivateKey
|
||||||
|
|
||||||
from src.types.ConditionVarPair import ConditionVarPair
|
from src.types.condition_var_pair import ConditionVarPair
|
||||||
from src.types.condition_opcodes import ConditionOpcode
|
from src.types.condition_opcodes import ConditionOpcode
|
||||||
from src.types.hashable.Program import Program
|
from src.types.hashable.program import Program
|
||||||
from src.types.hashable.BLSSignature import BLSSignature
|
from src.types.hashable.BLSSignature import BLSSignature
|
||||||
from src.types.hashable.Coin import Coin
|
from src.types.hashable.coin import Coin
|
||||||
from src.types.hashable.CoinSolution import CoinSolution
|
from src.types.hashable.coin_solution import CoinSolution
|
||||||
from src.types.hashable.SpendBundle import SpendBundle
|
from src.types.hashable.spend_bundle import SpendBundle
|
||||||
from src.util.condition_tools import (
|
from src.util.condition_tools import (
|
||||||
conditions_by_opcode,
|
conditions_by_opcode,
|
||||||
hash_key_pairs_for_conditions_dict,
|
hash_key_pairs_for_conditions_dict,
|
||||||
|
|
Loading…
Reference in New Issue