mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 16:08:51 +03:00
Merge remote-tracking branch 'origin/long_lived/atari' into dl_offer_hooks
This commit is contained in:
commit
e27f48159f
3
.github/workflows/benchmarks.yml
vendored
3
.github/workflows/benchmarks.yml
vendored
@ -29,6 +29,7 @@ jobs:
|
||||
python-version: [ 3.9 ]
|
||||
env:
|
||||
CHIA_ROOT: ${{ github.workspace }}/.chia/mainnet
|
||||
BLOCKS_AND_PLOTS_VERSION: 0.29.0
|
||||
|
||||
steps:
|
||||
- name: Clean workspace
|
||||
@ -62,7 +63,7 @@ jobs:
|
||||
with:
|
||||
repository: 'Chia-Network/test-cache'
|
||||
path: '.chia'
|
||||
ref: '0.29.0'
|
||||
ref: ${{ env.BLOCKS_AND_PLOTS_VERSION }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run install script
|
||||
|
11
.github/workflows/test-single.yml
vendored
11
.github/workflows/test-single.yml
vendored
@ -97,6 +97,7 @@ jobs:
|
||||
env:
|
||||
CHIA_ROOT: ${{ github.workspace }}/.chia/mainnet
|
||||
JOB_FILE_NAME: tests_${{ matrix.os.file_name }}_python-${{ matrix.python.file_name }}_${{ matrix.configuration.name }}
|
||||
BLOCKS_AND_PLOTS_VERSION: 0.29.0
|
||||
|
||||
steps:
|
||||
- name: Configure git
|
||||
@ -152,21 +153,21 @@ jobs:
|
||||
path: |
|
||||
${{ github.workspace }}/.chia/blocks
|
||||
${{ github.workspace }}/.chia/test-plots
|
||||
key: 0.29.0
|
||||
key: ${{ env.BLOCKS_AND_PLOTS_VERSION }}
|
||||
|
||||
- name: Checkout test blocks and plots (macOS, Ubuntu)
|
||||
if: steps.test-blocks-plots.outputs.cache-hit != 'true' && (matrix.os.matrix == 'ubuntu' || matrix.os.matrix == 'macos')
|
||||
run: |
|
||||
wget -qO- https://github.com/Chia-Network/test-cache/archive/refs/tags/0.29.0.tar.gz | tar xzf -
|
||||
wget -qO- https://github.com/Chia-Network/test-cache/archive/refs/tags/${{ env.BLOCKS_AND_PLOTS_VERSION }}.tar.gz | tar xzf -
|
||||
mkdir ${{ github.workspace }}/.chia
|
||||
mv ${{ github.workspace }}/test-cache-0.29.0/* ${{ github.workspace }}/.chia
|
||||
mv ${{ github.workspace }}/test-cache-${{ env.BLOCKS_AND_PLOTS_VERSION }}/* ${{ github.workspace }}/.chia
|
||||
|
||||
- name: Checkout test blocks and plots (Windows)
|
||||
if: steps.test-blocks-plots.outputs.cache-hit != 'true' && matrix.os.matrix == 'windows'
|
||||
run: |
|
||||
Invoke-WebRequest -OutFile blocks_and_plots.zip https://github.com/Chia-Network/test-cache/archive/refs/tags/0.29.0.zip; Expand-Archive blocks_and_plots.zip -DestinationPath .
|
||||
Invoke-WebRequest -OutFile blocks_and_plots.zip https://github.com/Chia-Network/test-cache/archive/refs/tags/${{ env.BLOCKS_AND_PLOTS_VERSION }}.zip; Expand-Archive blocks_and_plots.zip -DestinationPath .
|
||||
mkdir ${{ github.workspace }}/.chia
|
||||
mv ${{ github.workspace }}/test-cache-0.29.0/* ${{ github.workspace }}/.chia
|
||||
mv ${{ github.workspace }}/test-cache-${{ env.BLOCKS_AND_PLOTS_VERSION }}/* ${{ github.workspace }}/.chia
|
||||
|
||||
- name: Install boost (macOS)
|
||||
if: matrix.os.matrix == 'macos'
|
||||
|
32
build_scripts/npm_linux_deb/package-lock.json
generated
32
build_scripts/npm_linux_deb/package-lock.json
generated
@ -5194,9 +5194,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -5385,14 +5385,22 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
@ -12155,9 +12163,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -12298,9 +12306,9 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
|
44
build_scripts/npm_linux_rpm/package-lock.json
generated
44
build_scripts/npm_linux_rpm/package-lock.json
generated
@ -5036,9 +5036,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -5227,14 +5227,22 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
@ -6058,9 +6066,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/plist": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz",
|
||||
"integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"xmlbuilder": "^9.0.7"
|
||||
@ -11851,9 +11859,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -11994,9 +12002,9 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
@ -12624,9 +12632,9 @@
|
||||
}
|
||||
},
|
||||
"plist": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.4.tgz",
|
||||
"integrity": "sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==",
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.5.tgz",
|
||||
"integrity": "sha512-83vX4eYdQp3vP9SxuYgEM/G/pJQqLUz/V/xzPrzruLs7fz7jxGQ1msZ/mg1nwZxUSuOp4sb+/bEIbRrbzZRxDA==",
|
||||
"requires": {
|
||||
"base64-js": "^1.5.1",
|
||||
"xmlbuilder": "^9.0.7"
|
||||
|
32
build_scripts/npm_macos/package-lock.json
generated
32
build_scripts/npm_macos/package-lock.json
generated
@ -6158,9 +6158,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -6372,14 +6372,22 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
@ -14054,9 +14062,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -14220,9 +14228,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
|
32
build_scripts/npm_macos_m1/package-lock.json
generated
32
build_scripts/npm_macos_m1/package-lock.json
generated
@ -6133,9 +6133,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -6344,14 +6344,22 @@
|
||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
@ -13989,9 +13997,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -14152,9 +14160,9 @@
|
||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
|
32
build_scripts/npm_windows/package-lock.json
generated
32
build_scripts/npm_windows/package-lock.json
generated
@ -4974,9 +4974,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -5165,14 +5165,22 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
@ -11722,9 +11730,9 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"minimist-options": {
|
||||
"version": "4.1.0",
|
||||
@ -11865,9 +11873,9 @@
|
||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz",
|
||||
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==",
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
|
@ -12,9 +12,9 @@ def passphrase_cmd():
|
||||
|
||||
@passphrase_cmd.command(
|
||||
"set",
|
||||
help="""Sets or updates the keyring passphrase. If --passphrase-file and/or --current-passphrase-file options are provided,
|
||||
the passphrases will be read from the specified files. Otherwise, a prompt will be provided to enter the
|
||||
passphrase.""",
|
||||
help="""Sets or updates the keyring passphrase. If --passphrase-file and/or --current-passphrase-file options are
|
||||
provided, the passphrases will be read from the specified files. Otherwise, a prompt will be provided to
|
||||
enter the passphrase.""",
|
||||
short_help="Set or update the keyring passphrase",
|
||||
)
|
||||
@click.option("--passphrase-file", type=click.File("r"), help="File or descriptor to read the passphrase from")
|
||||
@ -70,8 +70,8 @@ def set_cmd(
|
||||
|
||||
@passphrase_cmd.command(
|
||||
"remove",
|
||||
help="""Remove the keyring passphrase. If the --current-passphrase-file option is provided, the passphrase will be read from
|
||||
the specified file. Otherwise, a prompt will be provided to enter the passphrase.""",
|
||||
help="""Remove the keyring passphrase. If the --current-passphrase-file option is provided, the passphrase will be
|
||||
read from the specified file. Otherwise, a prompt will be provided to enter the passphrase.""",
|
||||
short_help="Remove the keyring passphrase",
|
||||
)
|
||||
@click.option(
|
||||
|
@ -71,7 +71,7 @@ class BlockchainInterface:
|
||||
return None
|
||||
|
||||
async def persist_sub_epoch_challenge_segments(
|
||||
self, sub_epoch_summary_height: bytes32, segments: List[SubEpochChallengeSegment]
|
||||
self, sub_epoch_summary_hash: bytes32, segments: List[SubEpochChallengeSegment]
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
@ -244,7 +244,6 @@ class WebSocketServer:
|
||||
self.log.error(f"Unexpected exception trying to send to websocket: {e} {tb}")
|
||||
self.remove_connection(socket)
|
||||
await socket.close()
|
||||
break
|
||||
else:
|
||||
service_name = "Unknown"
|
||||
if ws in self.remote_address_map:
|
||||
|
@ -180,7 +180,7 @@ class PoolWallet:
|
||||
raise ValueError(f"Invalid internal Pool State: {err}: {initial_target_state}")
|
||||
|
||||
async def get_spend_history(self) -> List[Tuple[uint32, CoinSpend]]:
|
||||
return self.wallet_state_manager.pool_store.get_spends_for_wallet(self.wallet_id)
|
||||
return await self.wallet_state_manager.pool_store.get_spends_for_wallet(self.wallet_id)
|
||||
|
||||
async def get_current_state(self) -> PoolWalletInfo:
|
||||
history: List[Tuple[uint32, CoinSpend]] = await self.get_spend_history()
|
||||
@ -228,7 +228,7 @@ class PoolWallet:
|
||||
return await self.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(self.wallet_id)
|
||||
|
||||
async def get_tip(self) -> Tuple[uint32, CoinSpend]:
|
||||
return self.wallet_state_manager.pool_store.get_spends_for_wallet(self.wallet_id)[-1]
|
||||
return (await self.wallet_state_manager.pool_store.get_spends_for_wallet(self.wallet_id))[-1]
|
||||
|
||||
async def update_pool_config(self) -> None:
|
||||
current_state: PoolWalletInfo = await self.get_current_state()
|
||||
@ -286,7 +286,9 @@ class PoolWallet:
|
||||
self.log.info(f"New PoolWallet singleton tip_coin: {tip_spend} farmed at height {block_height}")
|
||||
|
||||
# If we have reached the target state, resets it to None. Loops back to get current state
|
||||
for _, added_spend in reversed(self.wallet_state_manager.pool_store.get_spends_for_wallet(self.wallet_id)):
|
||||
for _, added_spend in reversed(
|
||||
await self.wallet_state_manager.pool_store.get_spends_for_wallet(self.wallet_id)
|
||||
):
|
||||
latest_state: Optional[PoolState] = solution_to_pool_state(added_spend)
|
||||
if latest_state is not None:
|
||||
if self.target_state == latest_state:
|
||||
@ -303,9 +305,9 @@ class PoolWallet:
|
||||
Returns True if the wallet should be removed.
|
||||
"""
|
||||
try:
|
||||
history: List[Tuple[uint32, CoinSpend]] = self.wallet_state_manager.pool_store.get_spends_for_wallet(
|
||||
history: List[Tuple[uint32, CoinSpend]] = await self.wallet_state_manager.pool_store.get_spends_for_wallet(
|
||||
self.wallet_id
|
||||
).copy()
|
||||
)
|
||||
prev_state: PoolWalletInfo = await self.get_current_state()
|
||||
await self.wallet_state_manager.pool_store.rollback(block_height, self.wallet_id, in_transaction)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import ipaddress
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.seeder.crawler import Crawler
|
||||
from chia.util.ws_message import WsRpcMessage, create_payload_dict
|
||||
|
||||
@ -28,7 +28,7 @@ class CrawlerRpcApi:
|
||||
|
||||
return payloads
|
||||
|
||||
async def get_peer_counts(self, _request: Dict) -> Dict[str, Any]:
|
||||
async def get_peer_counts(self, _request: Dict) -> EndpointResult:
|
||||
ipv6_addresses_count = 0
|
||||
for host in self.service.best_timestamp_per_peer.keys():
|
||||
try:
|
||||
@ -52,7 +52,7 @@ class CrawlerRpcApi:
|
||||
}
|
||||
return data
|
||||
|
||||
async def get_ips_after_timestamp(self, _request: Dict) -> Dict[str, Any]:
|
||||
async def get_ips_after_timestamp(self, _request: Dict) -> EndpointResult:
|
||||
after = _request.get("after", None)
|
||||
if after is None:
|
||||
raise ValueError("`after` is required and must be a unix timestamp")
|
||||
|
@ -4,7 +4,7 @@ from pathlib import Path
|
||||
|
||||
from chia.data_layer.data_layer import DataLayer
|
||||
from chia.data_layer.data_layer_util import Side, Subscription
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
@ -73,20 +73,20 @@ class DataLayerRpcApi:
|
||||
"/add_missing_files": self.add_missing_files,
|
||||
}
|
||||
|
||||
async def create_data_store(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def create_data_store(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
if self.service is None:
|
||||
raise Exception("Data layer not created")
|
||||
fee = get_fee(self.service.config, request)
|
||||
txs, value = await self.service.create_store(uint64(fee))
|
||||
return {"txs": txs, "id": value.hex()}
|
||||
|
||||
async def get_owned_stores(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_owned_stores(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
if self.service is None:
|
||||
raise Exception("Data layer not created")
|
||||
singleton_records = await self.service.get_owned_stores()
|
||||
return {"store_ids": [singleton.launcher_id.hex() for singleton in singleton_records]}
|
||||
|
||||
async def get_value(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_value(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
store_id = bytes32.from_hexstr(request["id"])
|
||||
key = hexstr_to_bytes(request["key"])
|
||||
if self.service is None:
|
||||
@ -97,7 +97,7 @@ class DataLayerRpcApi:
|
||||
hex = value.hex()
|
||||
return {"value": hex}
|
||||
|
||||
async def get_keys_values(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_keys_values(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
store_id = bytes32(hexstr_to_bytes(request["id"]))
|
||||
root_hash = request.get("root_hash")
|
||||
if root_hash is not None:
|
||||
@ -111,7 +111,7 @@ class DataLayerRpcApi:
|
||||
json_nodes.append(json)
|
||||
return {"keys_values": json_nodes}
|
||||
|
||||
async def get_ancestors(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_ancestors(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
store_id = bytes32(hexstr_to_bytes(request["id"]))
|
||||
node_hash = bytes32.from_hexstr(request["hash"])
|
||||
if self.service is None:
|
||||
@ -119,7 +119,7 @@ class DataLayerRpcApi:
|
||||
value = await self.service.get_ancestors(node_hash, store_id)
|
||||
return {"ancestors": value}
|
||||
|
||||
async def batch_update(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def batch_update(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
id - the id of the store we are operating on
|
||||
changelist - a list of changes to apply on store
|
||||
@ -135,7 +135,7 @@ class DataLayerRpcApi:
|
||||
raise Exception(f"Batch update failed for: {store_id}")
|
||||
return {"tx_id": transaction_record.name}
|
||||
|
||||
async def insert(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def insert(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
rows_to_add a list of clvm objects as bytes to add to talbe
|
||||
rows_to_remove a list of row hashes to remove
|
||||
@ -151,7 +151,7 @@ class DataLayerRpcApi:
|
||||
transaction_record = await self.service.batch_update(store_id, changelist, uint64(fee))
|
||||
return {"tx_id": transaction_record.name}
|
||||
|
||||
async def delete_key(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def delete_key(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
rows_to_add a list of clvm objects as bytes to add to talbe
|
||||
rows_to_remove a list of row hashes to remove
|
||||
@ -166,7 +166,7 @@ class DataLayerRpcApi:
|
||||
transaction_record = await self.service.batch_update(store_id, changelist, uint64(fee))
|
||||
return {"tx_id": transaction_record.name}
|
||||
|
||||
async def get_root(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_root(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""get hash of latest tree root"""
|
||||
store_id = bytes32(hexstr_to_bytes(request["id"]))
|
||||
# todo input checks
|
||||
@ -177,7 +177,7 @@ class DataLayerRpcApi:
|
||||
raise Exception(f"Failed to get root for {store_id.hex()}")
|
||||
return {"hash": rec.root, "confirmed": rec.confirmed, "timestamp": rec.timestamp}
|
||||
|
||||
async def get_local_root(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_local_root(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""get hash of latest tree root saved in our local datastore"""
|
||||
store_id = bytes32(hexstr_to_bytes(request["id"]))
|
||||
# todo input checks
|
||||
@ -186,7 +186,7 @@ class DataLayerRpcApi:
|
||||
res = await self.service.get_local_root(store_id)
|
||||
return {"hash": res}
|
||||
|
||||
async def get_roots(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_roots(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
get state hashes for a list of roots
|
||||
"""
|
||||
@ -202,7 +202,7 @@ class DataLayerRpcApi:
|
||||
roots.append({"id": id_bytes, "hash": rec.root, "confirmed": rec.confirmed, "timestamp": rec.timestamp})
|
||||
return {"root_hashes": roots}
|
||||
|
||||
async def subscribe(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def subscribe(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
subscribe to singleton
|
||||
"""
|
||||
@ -217,7 +217,7 @@ class DataLayerRpcApi:
|
||||
await self.service.subscribe(store_id=store_id_bytes, urls=urls)
|
||||
return {}
|
||||
|
||||
async def unsubscribe(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def unsubscribe(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
unsubscribe from singleton
|
||||
"""
|
||||
@ -230,7 +230,7 @@ class DataLayerRpcApi:
|
||||
await self.service.unsubscribe(store_id_bytes)
|
||||
return {}
|
||||
|
||||
async def subscriptions(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def subscriptions(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
List current subscriptions
|
||||
"""
|
||||
@ -239,7 +239,7 @@ class DataLayerRpcApi:
|
||||
subscriptions: List[Subscription] = await self.service.get_subscriptions()
|
||||
return {"store_ids": [sub.tree_id.hex() for sub in subscriptions]}
|
||||
|
||||
async def add_missing_files(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def add_missing_files(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
complete the data server files.
|
||||
"""
|
||||
@ -257,7 +257,7 @@ class DataLayerRpcApi:
|
||||
await self.service.add_missing_files(tree_id, override, foldername)
|
||||
return {}
|
||||
|
||||
async def get_root_history(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_root_history(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
get history of state hashes for a store
|
||||
"""
|
||||
@ -271,7 +271,7 @@ class DataLayerRpcApi:
|
||||
res.insert(0, {"root_hash": rec.root, "confirmed": rec.confirmed, "timestamp": rec.timestamp})
|
||||
return {"root_history": res}
|
||||
|
||||
async def get_kv_diff(self, request: Dict[str, Any]) -> Dict[str, Any]:
|
||||
async def get_kv_diff(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
get kv diff between two root hashes
|
||||
"""
|
||||
|
@ -7,7 +7,7 @@ from typing_extensions import Protocol
|
||||
from chia.farmer.farmer import Farmer
|
||||
from chia.plot_sync.receiver import Receiver
|
||||
from chia.protocols.harvester_protocol import Plot
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.ints import uint32
|
||||
@ -161,7 +161,7 @@ class FarmerRpcApi:
|
||||
|
||||
return payloads
|
||||
|
||||
async def get_signage_point(self, request: Dict) -> Dict:
|
||||
async def get_signage_point(self, request: Dict) -> EndpointResult:
|
||||
sp_hash = hexstr_to_bytes(request["sp_hash"])
|
||||
for _, sps in self.service.sps.items():
|
||||
for sp in sps:
|
||||
@ -180,7 +180,7 @@ class FarmerRpcApi:
|
||||
}
|
||||
raise ValueError(f"Signage point {sp_hash.hex()} not found")
|
||||
|
||||
async def get_signage_points(self, _: Dict) -> Dict[str, Any]:
|
||||
async def get_signage_points(self, _: Dict) -> EndpointResult:
|
||||
result: List[Dict[str, Any]] = []
|
||||
for sps in self.service.sps.values():
|
||||
for sp in sps:
|
||||
@ -200,12 +200,12 @@ class FarmerRpcApi:
|
||||
)
|
||||
return {"signage_points": result}
|
||||
|
||||
async def get_reward_targets(self, request: Dict) -> Dict:
|
||||
async def get_reward_targets(self, request: Dict) -> EndpointResult:
|
||||
search_for_private_key = request["search_for_private_key"]
|
||||
max_ph_to_search = request.get("max_ph_to_search", 500)
|
||||
return await self.service.get_reward_targets(search_for_private_key, max_ph_to_search)
|
||||
|
||||
async def set_reward_targets(self, request: Dict) -> Dict:
|
||||
async def set_reward_targets(self, request: Dict) -> EndpointResult:
|
||||
farmer_target, pool_target = None, None
|
||||
if "farmer_target" in request:
|
||||
farmer_target = request["farmer_target"]
|
||||
@ -223,7 +223,7 @@ class FarmerRpcApi:
|
||||
)
|
||||
return plot_count
|
||||
|
||||
async def get_pool_state(self, _: Dict) -> Dict:
|
||||
async def get_pool_state(self, _: Dict) -> EndpointResult:
|
||||
pools_list = []
|
||||
for p2_singleton_puzzle_hash, pool_dict in self.service.pool_state.items():
|
||||
pool_state = pool_dict.copy()
|
||||
@ -232,18 +232,18 @@ class FarmerRpcApi:
|
||||
pools_list.append(pool_state)
|
||||
return {"pool_state": pools_list}
|
||||
|
||||
async def set_payout_instructions(self, request: Dict) -> Dict:
|
||||
async def set_payout_instructions(self, request: Dict) -> EndpointResult:
|
||||
launcher_id: bytes32 = bytes32.from_hexstr(request["launcher_id"])
|
||||
await self.service.set_payout_instructions(launcher_id, request["payout_instructions"])
|
||||
return {}
|
||||
|
||||
async def get_harvesters(self, _: Dict):
|
||||
async def get_harvesters(self, _: Dict) -> EndpointResult:
|
||||
return await self.service.get_harvesters(False)
|
||||
|
||||
async def get_harvesters_summary(self, _: Dict[str, object]) -> Dict[str, object]:
|
||||
async def get_harvesters_summary(self, _: Dict[str, object]) -> EndpointResult:
|
||||
return await self.service.get_harvesters(True)
|
||||
|
||||
async def get_harvester_plots_valid(self, request_dict: Dict[str, object]) -> Dict[str, object]:
|
||||
async def get_harvester_plots_valid(self, request_dict: Dict[str, object]) -> EndpointResult:
|
||||
# TODO: Consider having a extra List[PlotInfo] in Receiver to avoid rebuilding the list for each call
|
||||
request = PlotInfoRequestData.from_json_dict(request_dict)
|
||||
plot_list = list(self.service.get_receiver(request.node_id).plots().values())
|
||||
@ -271,16 +271,16 @@ class FarmerRpcApi:
|
||||
source = sorted(source, reverse=request.reverse)
|
||||
return paginated_plot_request(source, request)
|
||||
|
||||
async def get_harvester_plots_invalid(self, request_dict: Dict[str, object]) -> Dict[str, object]:
|
||||
async def get_harvester_plots_invalid(self, request_dict: Dict[str, object]) -> EndpointResult:
|
||||
return self.paginated_plot_path_request(Receiver.invalid, request_dict)
|
||||
|
||||
async def get_harvester_plots_keys_missing(self, request_dict: Dict[str, object]) -> Dict[str, object]:
|
||||
async def get_harvester_plots_keys_missing(self, request_dict: Dict[str, object]) -> EndpointResult:
|
||||
return self.paginated_plot_path_request(Receiver.keys_missing, request_dict)
|
||||
|
||||
async def get_harvester_plots_duplicates(self, request_dict: Dict[str, object]) -> Dict[str, object]:
|
||||
async def get_harvester_plots_duplicates(self, request_dict: Dict[str, object]) -> EndpointResult:
|
||||
return self.paginated_plot_path_request(Receiver.duplicates, request_dict)
|
||||
|
||||
async def get_pool_login_link(self, request: Dict) -> Dict:
|
||||
async def get_pool_login_link(self, request: Dict) -> EndpointResult:
|
||||
launcher_id: bytes32 = bytes32(hexstr_to_bytes(request["launcher_id"]))
|
||||
login_link: Optional[str] = await self.service.generate_login_link(launcher_id)
|
||||
if login_link is None:
|
||||
|
@ -4,7 +4,7 @@ from chia.consensus.block_record import BlockRecord
|
||||
from chia.consensus.pos_quality import UI_ACTUAL_SPACE_CONSTANT_FACTOR
|
||||
from chia.full_node.full_node import FullNode
|
||||
from chia.full_node.mempool_check_conditions import get_puzzle_and_solution_for_coin
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.types.blockchain_format.program import Program, SerializedProgram
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.coin_record import CoinRecord
|
||||
@ -96,11 +96,11 @@ class FullNodeRpcApi:
|
||||
|
||||
# this function is just here for backwards-compatibility. It will probably
|
||||
# be removed in the future
|
||||
async def get_initial_freeze_period(self, _: Dict):
|
||||
async def get_initial_freeze_period(self, _: Dict) -> EndpointResult:
|
||||
# Mon May 03 2021 17:00:00 GMT+0000
|
||||
return {"INITIAL_FREEZE_END_TIMESTAMP": 1620061200}
|
||||
|
||||
async def get_blockchain_state(self, _request: Dict):
|
||||
async def get_blockchain_state(self, _request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Returns a summary of the node's view of the blockchain.
|
||||
"""
|
||||
@ -214,12 +214,12 @@ class FullNodeRpcApi:
|
||||
self.cached_blockchain_state = dict(response["blockchain_state"])
|
||||
return response
|
||||
|
||||
async def get_network_info(self, request: Dict):
|
||||
async def get_network_info(self, request: Dict) -> EndpointResult:
|
||||
network_name = self.service.config["selected_network"]
|
||||
address_prefix = self.service.config["network_overrides"]["config"][network_name]["address_prefix"]
|
||||
return {"network_name": network_name, "network_prefix": address_prefix}
|
||||
|
||||
async def get_recent_signage_point_or_eos(self, request: Dict):
|
||||
async def get_recent_signage_point_or_eos(self, request: Dict) -> EndpointResult:
|
||||
if "sp_hash" not in request:
|
||||
challenge_hash: bytes32 = bytes32.from_hexstr(request["challenge_hash"])
|
||||
# This is the case of getting an end of slot
|
||||
@ -308,7 +308,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"signage_point": sp, "time_received": time_received, "reverted": True}
|
||||
|
||||
async def get_block(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_block(self, request: Dict) -> EndpointResult:
|
||||
if "header_hash" not in request:
|
||||
raise ValueError("No header_hash in request")
|
||||
header_hash = bytes32.from_hexstr(request["header_hash"])
|
||||
@ -319,7 +319,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"block": block}
|
||||
|
||||
async def get_blocks(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_blocks(self, request: Dict) -> EndpointResult:
|
||||
if "start" not in request:
|
||||
raise ValueError("No start in request")
|
||||
if "end" not in request:
|
||||
@ -349,7 +349,7 @@ class FullNodeRpcApi:
|
||||
json_blocks.append(json)
|
||||
return {"blocks": json_blocks}
|
||||
|
||||
async def get_block_count_metrics(self, request: Dict):
|
||||
async def get_block_count_metrics(self, request: Dict) -> EndpointResult:
|
||||
compact_blocks = 0
|
||||
uncompact_blocks = 0
|
||||
with log_exceptions(self.service.log, consume=True):
|
||||
@ -369,7 +369,7 @@ class FullNodeRpcApi:
|
||||
}
|
||||
}
|
||||
|
||||
async def get_block_records(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_block_records(self, request: Dict) -> EndpointResult:
|
||||
if "start" not in request:
|
||||
raise ValueError("No start in request")
|
||||
if "end" not in request:
|
||||
@ -399,7 +399,7 @@ class FullNodeRpcApi:
|
||||
records.append(record)
|
||||
return {"block_records": records}
|
||||
|
||||
async def get_block_record_by_height(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_block_record_by_height(self, request: Dict) -> EndpointResult:
|
||||
if "height" not in request:
|
||||
raise ValueError("No height in request")
|
||||
height = request["height"]
|
||||
@ -418,7 +418,7 @@ class FullNodeRpcApi:
|
||||
raise ValueError(f"Block {header_hash} does not exist")
|
||||
return {"block_record": record}
|
||||
|
||||
async def get_block_record(self, request: Dict):
|
||||
async def get_block_record(self, request: Dict) -> EndpointResult:
|
||||
if "header_hash" not in request:
|
||||
raise ValueError("header_hash not in request")
|
||||
header_hash_str = request["header_hash"]
|
||||
@ -432,7 +432,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"block_record": record}
|
||||
|
||||
async def get_unfinished_block_headers(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_unfinished_block_headers(self, request: Dict) -> EndpointResult:
|
||||
|
||||
peak: Optional[BlockRecord] = self.service.blockchain.get_peak()
|
||||
if peak is None:
|
||||
@ -453,7 +453,7 @@ class FullNodeRpcApi:
|
||||
response_headers.append(unfinished_header_block)
|
||||
return {"headers": response_headers}
|
||||
|
||||
async def get_network_space(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_network_space(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves an estimate of total space validating the chain
|
||||
between two block header hashes.
|
||||
@ -493,7 +493,7 @@ class FullNodeRpcApi:
|
||||
)
|
||||
return {"space": uint128(int(network_space_bytes_estimate))}
|
||||
|
||||
async def get_coin_records_by_puzzle_hash(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_coin_records_by_puzzle_hash(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves the coins for a given puzzlehash, by default returns unspent coins.
|
||||
"""
|
||||
@ -512,7 +512,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
async def get_coin_records_by_puzzle_hashes(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_coin_records_by_puzzle_hashes(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves the coins for a given puzzlehash, by default returns unspent coins.
|
||||
"""
|
||||
@ -534,7 +534,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
async def get_coin_record_by_name(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_coin_record_by_name(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves a coin record by it's name.
|
||||
"""
|
||||
@ -548,7 +548,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"coin_record": coin_record_dict_backwards_compat(coin_record.to_json_dict())}
|
||||
|
||||
async def get_coin_records_by_names(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_coin_records_by_names(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves the coins for given coin IDs, by default returns unspent coins.
|
||||
"""
|
||||
@ -570,7 +570,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
async def get_coin_records_by_parent_ids(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_coin_records_by_parent_ids(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves the coins for given parent coin IDs, by default returns unspent coins.
|
||||
"""
|
||||
@ -592,7 +592,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
async def get_coin_records_by_hint(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_coin_records_by_hint(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Retrieves coins by hint, by default returns unspent coins.
|
||||
"""
|
||||
@ -621,7 +621,7 @@ class FullNodeRpcApi:
|
||||
|
||||
return {"coin_records": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in coin_records]}
|
||||
|
||||
async def push_tx(self, request: Dict) -> Dict[str, object]:
|
||||
async def push_tx(self, request: Dict) -> EndpointResult:
|
||||
if "spend_bundle" not in request:
|
||||
raise ValueError("Spend bundle not in request")
|
||||
|
||||
@ -646,7 +646,7 @@ class FullNodeRpcApi:
|
||||
"status": status.name,
|
||||
}
|
||||
|
||||
async def get_puzzle_and_solution(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_puzzle_and_solution(self, request: Dict) -> EndpointResult:
|
||||
coin_name: bytes32 = bytes32.from_hexstr(request["coin_id"])
|
||||
height = request["height"]
|
||||
coin_record = await self.service.coin_store.get_coin_record(coin_name)
|
||||
@ -672,7 +672,7 @@ class FullNodeRpcApi:
|
||||
solution_ser: SerializedProgram = SerializedProgram.from_program(Program.to(solution))
|
||||
return {"coin_solution": CoinSpend(coin_record.coin, puzzle_ser, solution_ser)}
|
||||
|
||||
async def get_additions_and_removals(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_additions_and_removals(self, request: Dict) -> EndpointResult:
|
||||
if "header_hash" not in request:
|
||||
raise ValueError("No header_hash in request")
|
||||
header_hash = bytes32.from_hexstr(request["header_hash"])
|
||||
@ -692,17 +692,17 @@ class FullNodeRpcApi:
|
||||
"removals": [coin_record_dict_backwards_compat(cr.to_json_dict()) for cr in removals],
|
||||
}
|
||||
|
||||
async def get_all_mempool_tx_ids(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_all_mempool_tx_ids(self, request: Dict) -> EndpointResult:
|
||||
ids = list(self.service.mempool_manager.mempool.spends.keys())
|
||||
return {"tx_ids": ids}
|
||||
|
||||
async def get_all_mempool_items(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_all_mempool_items(self, request: Dict) -> EndpointResult:
|
||||
spends = {}
|
||||
for tx_id, item in self.service.mempool_manager.mempool.spends.items():
|
||||
spends[tx_id.hex()] = item
|
||||
return {"mempool_items": spends}
|
||||
|
||||
async def get_mempool_item_by_tx_id(self, request: Dict) -> Dict[str, object]:
|
||||
async def get_mempool_item_by_tx_id(self, request: Dict) -> EndpointResult:
|
||||
if "tx_id" not in request:
|
||||
raise ValueError("No tx_id in request")
|
||||
tx_id: bytes32 = bytes32.from_hexstr(request["tx_id"])
|
||||
|
@ -1,7 +1,7 @@
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from chia.harvester.harvester import Harvester
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.util.ws_message import WsRpcMessage, create_payload_dict
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ class HarvesterRpcApi:
|
||||
|
||||
return payloads
|
||||
|
||||
async def get_plots(self, request: Dict) -> Dict:
|
||||
async def get_plots(self, request: Dict) -> EndpointResult:
|
||||
plots, failed_to_open, not_found = self.service.get_plots()
|
||||
return {
|
||||
"plots": plots,
|
||||
@ -44,27 +44,27 @@ class HarvesterRpcApi:
|
||||
"not_found_filenames": not_found,
|
||||
}
|
||||
|
||||
async def refresh_plots(self, request: Dict) -> Dict:
|
||||
async def refresh_plots(self, request: Dict) -> EndpointResult:
|
||||
self.service.plot_manager.trigger_refresh()
|
||||
return {}
|
||||
|
||||
async def delete_plot(self, request: Dict) -> Dict:
|
||||
async def delete_plot(self, request: Dict) -> EndpointResult:
|
||||
filename = request["filename"]
|
||||
if self.service.delete_plot(filename):
|
||||
return {}
|
||||
raise ValueError(f"Not able to delete file {filename}")
|
||||
|
||||
async def add_plot_directory(self, request: Dict) -> Dict:
|
||||
async def add_plot_directory(self, request: Dict) -> EndpointResult:
|
||||
directory_name = request["dirname"]
|
||||
if await self.service.add_plot_directory(directory_name):
|
||||
return {}
|
||||
raise ValueError(f"Did not add plot directory {directory_name}")
|
||||
|
||||
async def get_plot_directories(self, request: Dict) -> Dict:
|
||||
async def get_plot_directories(self, request: Dict) -> EndpointResult:
|
||||
plot_dirs = await self.service.get_plot_directories()
|
||||
return {"directories": plot_dirs}
|
||||
|
||||
async def remove_plot_directory(self, request: Dict) -> Dict:
|
||||
async def remove_plot_directory(self, request: Dict) -> EndpointResult:
|
||||
directory_name = request["dirname"]
|
||||
if await self.service.remove_plot_directory(directory_name):
|
||||
return {}
|
||||
|
@ -26,7 +26,8 @@ log = logging.getLogger(__name__)
|
||||
max_message_size = 50 * 1024 * 1024 # 50MB
|
||||
|
||||
|
||||
Endpoint = Callable[[Dict[str, object]], Awaitable[Dict[str, object]]]
|
||||
EndpointResult = Dict[str, Any]
|
||||
Endpoint = Callable[[Dict[str, object]], Awaitable[EndpointResult]]
|
||||
|
||||
|
||||
class RpcApiProtocol(Protocol):
|
||||
@ -112,13 +113,13 @@ class RpcServer:
|
||||
"/healthz": self.healthz,
|
||||
}
|
||||
|
||||
async def _get_routes(self, request: Dict[str, Any]) -> Dict[str, object]:
|
||||
async def _get_routes(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
return {
|
||||
"success": "true",
|
||||
"routes": list(self.get_routes().keys()),
|
||||
}
|
||||
|
||||
async def get_connections(self, request: Dict[str, Any]) -> Dict[str, object]:
|
||||
async def get_connections(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
request_node_type: Optional[NodeType] = None
|
||||
if "node_type" in request:
|
||||
request_node_type = NodeType(request["node_type"])
|
||||
@ -174,7 +175,7 @@ class RpcServer:
|
||||
]
|
||||
return {"connections": con_info}
|
||||
|
||||
async def open_connection(self, request: Dict[str, Any]) -> Dict[str, object]:
|
||||
async def open_connection(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
host = request["host"]
|
||||
port = request["port"]
|
||||
target_node: PeerInfo = PeerInfo(host, uint16(int(port)))
|
||||
@ -187,7 +188,7 @@ class RpcServer:
|
||||
raise ValueError("Start client failed, or server is not set")
|
||||
return {}
|
||||
|
||||
async def close_connection(self, request: Dict[str, Any]) -> Dict[str, object]:
|
||||
async def close_connection(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
node_id = hexstr_to_bytes(request["node_id"])
|
||||
if self.rpc_api.service.server is None:
|
||||
raise web.HTTPInternalServerError()
|
||||
@ -198,7 +199,7 @@ class RpcServer:
|
||||
await connection.close()
|
||||
return {}
|
||||
|
||||
async def stop_node(self, request: Dict[str, Any]) -> Dict[str, object]:
|
||||
async def stop_node(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
"""
|
||||
Shuts down the node.
|
||||
"""
|
||||
@ -206,19 +207,19 @@ class RpcServer:
|
||||
self.stop_cb()
|
||||
return {}
|
||||
|
||||
async def healthz(self, request: Dict[str, Any]) -> Dict[str, object]:
|
||||
async def healthz(self, request: Dict[str, Any]) -> EndpointResult:
|
||||
return {
|
||||
"success": "true",
|
||||
}
|
||||
|
||||
async def ws_api(self, message: WsRpcMessage) -> Dict[str, object]:
|
||||
async def ws_api(self, message: WsRpcMessage) -> Optional[Dict[str, object]]:
|
||||
"""
|
||||
This function gets called when new message is received via websocket.
|
||||
"""
|
||||
|
||||
command = message["command"]
|
||||
if message["ack"]:
|
||||
return {}
|
||||
return None
|
||||
|
||||
data: Dict[str, object] = {}
|
||||
if "data" in message:
|
||||
|
@ -3,7 +3,7 @@ import dataclasses
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
from blspy import G1Element, G2Element, PrivateKey
|
||||
|
||||
@ -13,7 +13,7 @@ from chia.pools.pool_wallet import PoolWallet
|
||||
from chia.pools.pool_wallet_info import FARMING_TO_POOL, PoolState, PoolWalletInfo, create_pool_state
|
||||
from chia.protocols.protocol_message_types import ProtocolMessageTypes
|
||||
from chia.protocols.wallet_protocol import CoinState
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.server.outbound_message import NodeType, make_msg
|
||||
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
||||
from chia.types.announcement import Announcement
|
||||
@ -206,7 +206,7 @@ class WalletRpcApi:
|
||||
# Key management
|
||||
##########################################################################################
|
||||
|
||||
async def log_in(self, request):
|
||||
async def log_in(self, request) -> EndpointResult:
|
||||
"""
|
||||
Logs in the wallet with a specific key.
|
||||
"""
|
||||
@ -223,10 +223,10 @@ class WalletRpcApi:
|
||||
|
||||
return {"success": False, "error": "Unknown Error"}
|
||||
|
||||
async def get_logged_in_fingerprint(self, request: Dict):
|
||||
async def get_logged_in_fingerprint(self, request: Dict) -> EndpointResult:
|
||||
return {"fingerprint": self.service.logged_in_fingerprint}
|
||||
|
||||
async def get_public_keys(self, request: Dict):
|
||||
async def get_public_keys(self, request: Dict) -> EndpointResult:
|
||||
try:
|
||||
fingerprints = [
|
||||
sk.get_g1().get_fingerprint() for (sk, seed) in await self.service.keychain_proxy.get_all_private_keys()
|
||||
@ -248,7 +248,7 @@ class WalletRpcApi:
|
||||
log.error(f"Failed to get private key by fingerprint: {e}")
|
||||
return None, None
|
||||
|
||||
async def get_private_key(self, request):
|
||||
async def get_private_key(self, request) -> EndpointResult:
|
||||
fingerprint = request["fingerprint"]
|
||||
sk, seed = await self._get_private_key(fingerprint)
|
||||
if sk is not None:
|
||||
@ -265,10 +265,10 @@ class WalletRpcApi:
|
||||
}
|
||||
return {"success": False, "private_key": {"fingerprint": fingerprint}}
|
||||
|
||||
async def generate_mnemonic(self, request: Dict):
|
||||
async def generate_mnemonic(self, request: Dict) -> EndpointResult:
|
||||
return {"mnemonic": generate_mnemonic().split(" ")}
|
||||
|
||||
async def add_key(self, request):
|
||||
async def add_key(self, request) -> EndpointResult:
|
||||
if "mnemonic" not in request:
|
||||
raise ValueError("Mnemonic not in request")
|
||||
|
||||
@ -300,7 +300,7 @@ class WalletRpcApi:
|
||||
return {"fingerprint": fingerprint}
|
||||
raise ValueError("Failed to start")
|
||||
|
||||
async def delete_key(self, request):
|
||||
async def delete_key(self, request) -> EndpointResult:
|
||||
await self._stop_wallet()
|
||||
fingerprint = request["fingerprint"]
|
||||
try:
|
||||
@ -340,7 +340,7 @@ class WalletRpcApi:
|
||||
|
||||
return found_farmer, found_pool
|
||||
|
||||
async def check_delete_key(self, request):
|
||||
async def check_delete_key(self, request) -> EndpointResult:
|
||||
"""Check the key use prior to possible deletion
|
||||
checks whether key is used for either farm or pool rewards
|
||||
checks if any wallets have a non-zero balance
|
||||
@ -379,7 +379,7 @@ class WalletRpcApi:
|
||||
"wallet_balance": walletBalance,
|
||||
}
|
||||
|
||||
async def delete_all_keys(self, request: Dict):
|
||||
async def delete_all_keys(self, request: Dict) -> EndpointResult:
|
||||
await self._stop_wallet()
|
||||
try:
|
||||
await self.service.keychain_proxy.delete_all_keys()
|
||||
@ -395,28 +395,28 @@ class WalletRpcApi:
|
||||
# Wallet Node
|
||||
##########################################################################################
|
||||
|
||||
async def get_sync_status(self, request: Dict):
|
||||
async def get_sync_status(self, request: Dict) -> EndpointResult:
|
||||
syncing = self.service.wallet_state_manager.sync_mode
|
||||
synced = await self.service.wallet_state_manager.synced()
|
||||
return {"synced": synced, "syncing": syncing, "genesis_initialized": True}
|
||||
|
||||
async def get_height_info(self, request: Dict):
|
||||
async def get_height_info(self, request: Dict) -> EndpointResult:
|
||||
height = await self.service.wallet_state_manager.blockchain.get_finished_sync_up_to()
|
||||
return {"height": height}
|
||||
|
||||
async def get_network_info(self, request: Dict):
|
||||
async def get_network_info(self, request: Dict) -> EndpointResult:
|
||||
network_name = self.service.config["selected_network"]
|
||||
address_prefix = self.service.config["network_overrides"]["config"][network_name]["address_prefix"]
|
||||
return {"network_name": network_name, "network_prefix": address_prefix}
|
||||
|
||||
async def push_tx(self, request: Dict):
|
||||
async def push_tx(self, request: Dict) -> EndpointResult:
|
||||
nodes = self.service.server.get_full_node_connections()
|
||||
if len(nodes) == 0:
|
||||
raise ValueError("Wallet is not currently connected to any full node peers")
|
||||
await self.service.push_tx(SpendBundle.from_bytes(hexstr_to_bytes(request["spend_bundle"])))
|
||||
return {}
|
||||
|
||||
async def farm_block(self, request):
|
||||
async def farm_block(self, request) -> EndpointResult:
|
||||
raw_puzzle_hash = decode_puzzle_hash(request["address"])
|
||||
request = FarmNewBlockProtocol(raw_puzzle_hash)
|
||||
msg = make_msg(ProtocolMessageTypes.farm_new_block, request)
|
||||
@ -428,7 +428,7 @@ class WalletRpcApi:
|
||||
# Wallet Management
|
||||
##########################################################################################
|
||||
|
||||
async def get_wallets(self, request: Dict):
|
||||
async def get_wallets(self, request: Dict) -> EndpointResult:
|
||||
include_data: bool = request.get("include_data", True)
|
||||
wallet_type: Optional[WalletType] = None
|
||||
if "type" in request:
|
||||
@ -442,7 +442,7 @@ class WalletRpcApi:
|
||||
wallets = result
|
||||
return {"wallets": wallets}
|
||||
|
||||
async def create_new_wallet(self, request: Dict):
|
||||
async def create_new_wallet(self, request: Dict) -> EndpointResult:
|
||||
wallet_state_manager = self.service.wallet_state_manager
|
||||
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
@ -652,13 +652,14 @@ class WalletRpcApi:
|
||||
else: # undefined wallet_type
|
||||
pass
|
||||
|
||||
return None
|
||||
# TODO: rework this function to report detailed errors for each error case
|
||||
return {"success": False, "error": "invalid request"}
|
||||
|
||||
##########################################################################################
|
||||
# Wallet
|
||||
##########################################################################################
|
||||
|
||||
async def get_wallet_balance(self, request: Dict) -> Dict:
|
||||
async def get_wallet_balance(self, request: Dict) -> EndpointResult:
|
||||
wallet_id = uint32(int(request["wallet_id"]))
|
||||
wallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
|
||||
@ -710,7 +711,7 @@ class WalletRpcApi:
|
||||
|
||||
return {"wallet_balance": wallet_balance}
|
||||
|
||||
async def get_transaction(self, request: Dict) -> Dict:
|
||||
async def get_transaction(self, request: Dict) -> EndpointResult:
|
||||
transaction_id: bytes32 = bytes32(hexstr_to_bytes(request["transaction_id"]))
|
||||
tr: Optional[TransactionRecord] = await self.service.wallet_state_manager.get_transaction(transaction_id)
|
||||
if tr is None:
|
||||
@ -721,7 +722,7 @@ class WalletRpcApi:
|
||||
"transaction_id": tr.name,
|
||||
}
|
||||
|
||||
async def get_transactions(self, request: Dict) -> Dict:
|
||||
async def get_transactions(self, request: Dict) -> EndpointResult:
|
||||
wallet_id = int(request["wallet_id"])
|
||||
|
||||
start = request.get("start", 0)
|
||||
@ -745,7 +746,7 @@ class WalletRpcApi:
|
||||
"wallet_id": wallet_id,
|
||||
}
|
||||
|
||||
async def get_transaction_count(self, request: Dict) -> Dict:
|
||||
async def get_transaction_count(self, request: Dict) -> EndpointResult:
|
||||
wallet_id = int(request["wallet_id"])
|
||||
count = await self.service.wallet_state_manager.tx_store.get_transaction_count_for_wallet(wallet_id)
|
||||
return {
|
||||
@ -755,11 +756,11 @@ class WalletRpcApi:
|
||||
|
||||
# this function is just here for backwards-compatibility. It will probably
|
||||
# be removed in the future
|
||||
async def get_initial_freeze_period(self, _: Dict):
|
||||
async def get_initial_freeze_period(self, _: Dict) -> EndpointResult:
|
||||
# Mon May 03 2021 17:00:00 GMT+0000
|
||||
return {"INITIAL_FREEZE_END_TIMESTAMP": 1620061200}
|
||||
|
||||
async def get_next_address(self, request: Dict) -> Dict:
|
||||
async def get_next_address(self, request: Dict) -> EndpointResult:
|
||||
"""
|
||||
Returns a new address
|
||||
"""
|
||||
@ -785,11 +786,11 @@ class WalletRpcApi:
|
||||
"address": address,
|
||||
}
|
||||
|
||||
async def send_transaction(self, request):
|
||||
async def send_transaction(self, request) -> EndpointResult:
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
raise ValueError("Wallet needs to be fully synced before sending transactions")
|
||||
|
||||
wallet_id = int(request["wallet_id"])
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
|
||||
if wallet.type() == WalletType.CAT:
|
||||
@ -823,7 +824,7 @@ class WalletRpcApi:
|
||||
"transaction_id": tx.name,
|
||||
}
|
||||
|
||||
async def send_transaction_multi(self, request) -> Dict:
|
||||
async def send_transaction_multi(self, request) -> EndpointResult:
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
raise ValueError("Wallet needs to be fully synced before sending transactions")
|
||||
|
||||
@ -838,7 +839,7 @@ class WalletRpcApi:
|
||||
# Transaction may not have been included in the mempool yet. Use get_transaction to check.
|
||||
return {"transaction": transaction, "transaction_id": tr.name}
|
||||
|
||||
async def delete_unconfirmed_transactions(self, request):
|
||||
async def delete_unconfirmed_transactions(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
if wallet_id not in self.service.wallet_state_manager.wallets:
|
||||
raise ValueError(f"Wallet id {wallet_id} does not exist")
|
||||
@ -854,7 +855,7 @@ class WalletRpcApi:
|
||||
await self.service.wallet_state_manager.tx_store.db_wrapper.commit_transaction()
|
||||
return {}
|
||||
|
||||
async def select_coins(self, request) -> Dict[str, object]:
|
||||
async def select_coins(self, request) -> EndpointResult:
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
raise ValueError("Wallet needs to be fully synced before selecting coins")
|
||||
|
||||
@ -871,22 +872,22 @@ class WalletRpcApi:
|
||||
# CATs and Trading
|
||||
##########################################################################################
|
||||
|
||||
async def get_cat_list(self, request):
|
||||
async def get_cat_list(self, request) -> EndpointResult:
|
||||
return {"cat_list": list(DEFAULT_CATS.values())}
|
||||
|
||||
async def cat_set_name(self, request):
|
||||
wallet_id = int(request["wallet_id"])
|
||||
async def cat_set_name(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: CATWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
await wallet.set_name(str(request["name"]))
|
||||
return {"wallet_id": wallet_id}
|
||||
|
||||
async def cat_get_name(self, request):
|
||||
wallet_id = int(request["wallet_id"])
|
||||
async def cat_get_name(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: CATWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
name: str = await wallet.get_name()
|
||||
return {"wallet_id": wallet_id, "name": name}
|
||||
|
||||
async def get_stray_cats(self, request):
|
||||
async def get_stray_cats(self, request) -> EndpointResult:
|
||||
"""
|
||||
Get a list of all unacknowledged CATs
|
||||
:param request: RPC request
|
||||
@ -895,10 +896,10 @@ class WalletRpcApi:
|
||||
cats = await self.service.wallet_state_manager.interested_store.get_unacknowledged_tokens()
|
||||
return {"stray_cats": cats}
|
||||
|
||||
async def cat_spend(self, request):
|
||||
async def cat_spend(self, request) -> EndpointResult:
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
raise ValueError("Wallet needs to be fully synced.")
|
||||
wallet_id = int(request["wallet_id"])
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: CATWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
|
||||
puzzle_hash: bytes32 = decode_puzzle_hash(request["inner_address"])
|
||||
@ -914,7 +915,7 @@ class WalletRpcApi:
|
||||
else:
|
||||
fee = uint64(0)
|
||||
async with self.service.wallet_state_manager.lock:
|
||||
txs: TransactionRecord = await wallet.generate_signed_transaction(
|
||||
txs: List[TransactionRecord] = await wallet.generate_signed_transaction(
|
||||
[amount], [puzzle_hash], fee, memos=[memos]
|
||||
)
|
||||
for tx in txs:
|
||||
@ -925,13 +926,13 @@ class WalletRpcApi:
|
||||
"transaction_id": tx.name,
|
||||
}
|
||||
|
||||
async def cat_get_asset_id(self, request):
|
||||
wallet_id = int(request["wallet_id"])
|
||||
async def cat_get_asset_id(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: CATWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
asset_id: str = wallet.get_asset_id()
|
||||
return {"asset_id": asset_id, "wallet_id": wallet_id}
|
||||
|
||||
async def cat_asset_id_to_name(self, request):
|
||||
async def cat_asset_id_to_name(self, request) -> EndpointResult:
|
||||
wallet = await self.service.wallet_state_manager.get_wallet_for_asset_id(request["asset_id"])
|
||||
if wallet is None:
|
||||
if request["asset_id"] in DEFAULT_CATS:
|
||||
@ -941,7 +942,7 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"wallet_id": wallet.id(), "name": (await wallet.get_name())}
|
||||
|
||||
async def create_offer_for_ids(self, request):
|
||||
async def create_offer_for_ids(self, request) -> EndpointResult:
|
||||
offer: Dict[str, int] = request["offer"]
|
||||
fee: uint64 = uint64(request.get("fee", 0))
|
||||
validate_only: bool = request.get("validate_only", False)
|
||||
@ -962,7 +963,7 @@ class WalletRpcApi:
|
||||
for key, value in driver_dict_str.items():
|
||||
driver_dict[bytes32.from_hexstr(key)] = PuzzleInfo(value)
|
||||
|
||||
modified_offer = {}
|
||||
modified_offer: Dict[Union[int, bytes32], int] = {}
|
||||
for key in offer:
|
||||
try:
|
||||
modified_offer[bytes32.from_hexstr(key)] = offer[key]
|
||||
@ -970,21 +971,18 @@ class WalletRpcApi:
|
||||
modified_offer[int(key)] = offer[key]
|
||||
|
||||
async with self.service.wallet_state_manager.lock:
|
||||
(
|
||||
success,
|
||||
trade_record,
|
||||
error,
|
||||
) = await self.service.wallet_state_manager.trade_manager.create_offer_for_ids(
|
||||
modified_offer, driver_dict, request.get("solver", {}), fee=fee, validate_only=validate_only
|
||||
result = await self.service.wallet_state_manager.trade_manager.create_offer_for_ids(
|
||||
modified_offer, driver_dict, fee=fee, validate_only=validate_only
|
||||
)
|
||||
if success:
|
||||
if result[0]:
|
||||
success, trade_record, error = result
|
||||
return {
|
||||
"offer": Offer.from_bytes(trade_record.offer).to_bech32(),
|
||||
"trade_record": trade_record.to_json_dict_convenience(),
|
||||
}
|
||||
raise ValueError(error)
|
||||
raise ValueError(result[2])
|
||||
|
||||
async def get_offer_summary(self, request):
|
||||
async def get_offer_summary(self, request) -> EndpointResult:
|
||||
offer_hex: str = request["offer"]
|
||||
offer = Offer.from_bech32(offer_hex)
|
||||
offered, requested, infos = offer.summary()
|
||||
@ -996,26 +994,25 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"summary": await self.service.wallet_state_manager.trade_manager.get_offer_summary(offer)}
|
||||
|
||||
async def check_offer_validity(self, request):
|
||||
async def check_offer_validity(self, request) -> EndpointResult:
|
||||
offer_hex: str = request["offer"]
|
||||
offer = Offer.from_bech32(offer_hex)
|
||||
|
||||
return {"valid": (await self.service.wallet_state_manager.trade_manager.check_offer_validity(offer))}
|
||||
|
||||
async def take_offer(self, request):
|
||||
async def take_offer(self, request) -> EndpointResult:
|
||||
offer_hex: str = request["offer"]
|
||||
offer = Offer.from_bech32(offer_hex)
|
||||
fee: uint64 = uint64(request.get("fee", 0))
|
||||
|
||||
async with self.service.wallet_state_manager.lock:
|
||||
(success, trade_record, error,) = await self.service.wallet_state_manager.trade_manager.respond_to_offer(
|
||||
offer, request.get("solver", {}), fee=fee
|
||||
)
|
||||
if not success:
|
||||
raise ValueError(error)
|
||||
result = await self.service.wallet_state_manager.trade_manager.respond_to_offer(offer, fee=fee)
|
||||
if not result[0]:
|
||||
raise ValueError(result[2])
|
||||
success, trade_record, error = result
|
||||
return {"trade_record": trade_record.to_json_dict_convenience()}
|
||||
|
||||
async def get_offer(self, request: Dict):
|
||||
async def get_offer(self, request: Dict) -> EndpointResult:
|
||||
trade_mgr = self.service.wallet_state_manager.trade_manager
|
||||
|
||||
trade_id = bytes32.from_hexstr(request["trade_id"])
|
||||
@ -1028,7 +1025,7 @@ class WalletRpcApi:
|
||||
offer_value: Optional[str] = Offer.from_bytes(offer_to_return).to_bech32() if file_contents else None
|
||||
return {"trade_record": trade_record.to_json_dict_convenience(), "offer": offer_value}
|
||||
|
||||
async def get_all_offers(self, request: Dict):
|
||||
async def get_all_offers(self, request: Dict) -> EndpointResult:
|
||||
trade_mgr = self.service.wallet_state_manager.trade_manager
|
||||
|
||||
start: int = request.get("start", 0)
|
||||
@ -1059,14 +1056,14 @@ class WalletRpcApi:
|
||||
|
||||
return {"trade_records": result, "offers": offer_values}
|
||||
|
||||
async def get_offers_count(self, request: Dict):
|
||||
async def get_offers_count(self, request: Dict) -> EndpointResult:
|
||||
trade_mgr = self.service.wallet_state_manager.trade_manager
|
||||
|
||||
(total, my_offers_count, taken_offers_count) = await trade_mgr.trade_store.get_trades_count()
|
||||
|
||||
return {"total": total, "my_offers_count": my_offers_count, "taken_offers_count": taken_offers_count}
|
||||
|
||||
async def cancel_offer(self, request: Dict):
|
||||
async def cancel_offer(self, request: Dict) -> EndpointResult:
|
||||
wsm = self.service.wallet_state_manager
|
||||
secure = request["secure"]
|
||||
trade_id = bytes32.from_hexstr(request["trade_id"])
|
||||
@ -1083,7 +1080,7 @@ class WalletRpcApi:
|
||||
# Distributed Identities
|
||||
##########################################################################################
|
||||
|
||||
async def did_set_wallet_name(self, request):
|
||||
async def did_set_wallet_name(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
if wallet.type() == WalletType.DECENTRALIZED_ID:
|
||||
@ -1092,13 +1089,13 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"success": False, "error": f"Wallet id {wallet_id} is not a DID wallet"}
|
||||
|
||||
async def did_get_wallet_name(self, request):
|
||||
async def did_get_wallet_name(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
name: str = await wallet.get_name()
|
||||
return {"success": True, "wallet_id": wallet_id, "name": name}
|
||||
|
||||
async def did_update_recovery_ids(self, request):
|
||||
async def did_update_recovery_ids(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
recovery_list = []
|
||||
@ -1108,7 +1105,7 @@ class WalletRpcApi:
|
||||
if "num_verifications_required" in request:
|
||||
new_amount_verifications_required = uint64(request["num_verifications_required"])
|
||||
else:
|
||||
new_amount_verifications_required = len(recovery_list)
|
||||
new_amount_verifications_required = uint64(len(recovery_list))
|
||||
async with self.service.wallet_state_manager.lock:
|
||||
update_success = await wallet.update_recovery_list(recovery_list, new_amount_verifications_required)
|
||||
# Update coin with new ID info
|
||||
@ -1118,7 +1115,7 @@ class WalletRpcApi:
|
||||
success = True
|
||||
return {"success": success}
|
||||
|
||||
async def did_update_metadata(self, request):
|
||||
async def did_update_metadata(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
if wallet.type() != WalletType.DECENTRALIZED_ID.value:
|
||||
@ -1138,7 +1135,7 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"success": False, "error": f"Couldn't update metadata with input: {metadata}"}
|
||||
|
||||
async def did_get_did(self, request):
|
||||
async def did_get_did(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
my_did: str = encode_puzzle_hash(bytes32.fromhex(wallet.get_my_DID()), DID_HRP)
|
||||
@ -1150,7 +1147,7 @@ class WalletRpcApi:
|
||||
coin = coins.pop()
|
||||
return {"success": True, "wallet_id": wallet_id, "my_did": my_did, "coin_id": coin.name()}
|
||||
|
||||
async def did_get_recovery_list(self, request):
|
||||
async def did_get_recovery_list(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
recovery_list = wallet.did_info.backup_ids
|
||||
@ -1164,7 +1161,7 @@ class WalletRpcApi:
|
||||
"num_required": wallet.did_info.num_of_backup_ids_needed,
|
||||
}
|
||||
|
||||
async def did_get_metadata(self, request):
|
||||
async def did_get_metadata(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
metadata = json.loads(wallet.did_info.metadata)
|
||||
@ -1174,7 +1171,7 @@ class WalletRpcApi:
|
||||
"metadata": metadata,
|
||||
}
|
||||
|
||||
async def did_recovery_spend(self, request):
|
||||
async def did_recovery_spend(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
if len(request["attest_data"]) < wallet.did_info.num_of_backup_ids_needed:
|
||||
@ -1193,13 +1190,14 @@ class WalletRpcApi:
|
||||
pubkey = wallet.did_info.temp_pubkey
|
||||
|
||||
if "puzhash" in request:
|
||||
puzhash = hexstr_to_bytes(request["puzhash"])
|
||||
puzhash = bytes32.from_hexstr(request["puzhash"])
|
||||
else:
|
||||
assert wallet.did_info.temp_puzhash is not None
|
||||
puzhash = wallet.did_info.temp_puzhash
|
||||
|
||||
# TODO: this ignore should be dealt with
|
||||
spend_bundle = await wallet.recovery_spend(
|
||||
wallet.did_info.temp_coin,
|
||||
wallet.did_info.temp_coin, # type: ignore[arg-type]
|
||||
puzhash,
|
||||
info_list,
|
||||
pubkey,
|
||||
@ -1210,13 +1208,13 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"success": False}
|
||||
|
||||
async def did_get_pubkey(self, request):
|
||||
async def did_get_pubkey(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
pubkey = bytes((await wallet.wallet_state_manager.get_unused_derivation_record(wallet_id)).pubkey).hex()
|
||||
return {"success": True, "pubkey": pubkey}
|
||||
|
||||
async def did_create_attest(self, request):
|
||||
async def did_create_attest(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
async with self.service.wallet_state_manager.lock:
|
||||
@ -1238,11 +1236,12 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"success": False}
|
||||
|
||||
async def did_get_information_needed_for_recovery(self, request):
|
||||
async def did_get_information_needed_for_recovery(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
did_wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
my_did = encode_puzzle_hash(bytes32.from_hexstr(did_wallet.get_my_DID()), DID_HRP)
|
||||
coin_name = did_wallet.did_info.temp_coin.name().hex()
|
||||
# TODO: this ignore should be dealt with
|
||||
coin_name = did_wallet.did_info.temp_coin.name().hex() # type: ignore[union-attr]
|
||||
return {
|
||||
"success": True,
|
||||
"wallet_id": wallet_id,
|
||||
@ -1253,7 +1252,7 @@ class WalletRpcApi:
|
||||
"backup_dids": did_wallet.did_info.backup_ids,
|
||||
}
|
||||
|
||||
async def did_get_current_coin_info(self, request):
|
||||
async def did_get_current_coin_info(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
did_wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
my_did = encode_puzzle_hash(bytes32.from_hexstr(did_wallet.get_my_DID()), DID_HRP)
|
||||
@ -1269,12 +1268,12 @@ class WalletRpcApi:
|
||||
"did_amount": did_coin_threeple[2],
|
||||
}
|
||||
|
||||
async def did_create_backup_file(self, request):
|
||||
async def did_create_backup_file(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
did_wallet: DIDWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
return {"wallet_id": wallet_id, "success": True, "backup_data": did_wallet.create_backup()}
|
||||
|
||||
async def did_transfer_did(self, request):
|
||||
async def did_transfer_did(self, request) -> EndpointResult:
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
raise ValueError("Wallet needs to be fully synced.")
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
@ -1295,7 +1294,7 @@ class WalletRpcApi:
|
||||
# NFT Wallet
|
||||
##########################################################################################
|
||||
|
||||
async def nft_mint_nft(self, request) -> Dict:
|
||||
async def nft_mint_nft(self, request) -> EndpointResult:
|
||||
log.debug("Got minting RPC request: %s", request)
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
assert self.service.wallet_state_manager
|
||||
@ -1354,7 +1353,7 @@ class WalletRpcApi:
|
||||
)
|
||||
return {"wallet_id": wallet_id, "success": True, "spend_bundle": spend_bundle}
|
||||
|
||||
async def nft_get_nfts(self, request) -> Dict:
|
||||
async def nft_get_nfts(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
nft_wallet: NFTWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
nfts = nft_wallet.get_current_nfts()
|
||||
@ -1382,7 +1381,7 @@ class WalletRpcApi:
|
||||
log.exception(f"Failed to set DID on NFT: {e}")
|
||||
return {"success": False, "error": f"Failed to set DID on NFT: {e}"}
|
||||
|
||||
async def nft_get_by_did(self, request) -> Dict:
|
||||
async def nft_get_by_did(self, request) -> EndpointResult:
|
||||
did_id: Optional[bytes32] = None
|
||||
if "did_id" in request:
|
||||
did_id = decode_puzzle_hash(request["did_id"])
|
||||
@ -1391,7 +1390,7 @@ class WalletRpcApi:
|
||||
return {"wallet_id": wallet.wallet_id, "success": True}
|
||||
return {"error": f"Cannot find a NFT wallet DID = {did_id}", "success": False}
|
||||
|
||||
async def nft_get_wallet_did(self, request) -> Dict:
|
||||
async def nft_get_wallet_did(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
nft_wallet: NFTWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
if nft_wallet is not None:
|
||||
@ -1404,7 +1403,7 @@ class WalletRpcApi:
|
||||
return {"success": True, "did_id": None if len(did_id) == 0 else did_id}
|
||||
return {"success": False, "error": f"Wallet {wallet_id} not found"}
|
||||
|
||||
async def nft_get_wallets_with_dids(self, request) -> Dict:
|
||||
async def nft_get_wallets_with_dids(self, request) -> EndpointResult:
|
||||
all_wallets = self.service.wallet_state_manager.wallets.values()
|
||||
did_wallets_by_did_id: Dict[bytes32, uint32] = {
|
||||
wallet.did_info.origin_coin.name(): wallet.id()
|
||||
@ -1429,7 +1428,7 @@ class WalletRpcApi:
|
||||
)
|
||||
return {"success": True, "nft_wallets": did_nft_wallets}
|
||||
|
||||
async def nft_set_nft_status(self, request) -> Dict:
|
||||
async def nft_set_nft_status(self, request) -> EndpointResult:
|
||||
try:
|
||||
wallet_id: uint32 = uint32(request["wallet_id"])
|
||||
coin_id: bytes32 = bytes32.from_hexstr(request["coin_id"])
|
||||
@ -1443,7 +1442,7 @@ class WalletRpcApi:
|
||||
except Exception as e:
|
||||
return {"success": False, "error": f"Cannot change the status of the NFT.{e}"}
|
||||
|
||||
async def nft_transfer_nft(self, request):
|
||||
async def nft_transfer_nft(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
address = request["target_address"]
|
||||
if isinstance(address, str):
|
||||
@ -1478,7 +1477,7 @@ class WalletRpcApi:
|
||||
log.exception(f"Failed to transfer NFT: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
async def nft_get_info(self, request: Dict) -> Dict[str, object]:
|
||||
async def nft_get_info(self, request: Dict) -> EndpointResult:
|
||||
if "coin_id" not in request:
|
||||
return {"success": False, "error": "Coin ID is required."}
|
||||
coin_id = request["coin_id"]
|
||||
@ -1486,13 +1485,8 @@ class WalletRpcApi:
|
||||
coin_id = decode_puzzle_hash(coin_id)
|
||||
else:
|
||||
coin_id = bytes32.from_hexstr(coin_id)
|
||||
peer = self.service.wallet_state_manager.wallet_node.get_full_node_peer()
|
||||
if peer is None:
|
||||
return {"success": False, "error": "Cannot find a full node peer."}
|
||||
# Get coin state
|
||||
coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[coin_id], peer=peer
|
||||
)
|
||||
coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state([coin_id])
|
||||
if coin_state_list is None or len(coin_state_list) < 1:
|
||||
return {"success": False, "error": f"Coin record 0x{coin_id.hex()} not found"}
|
||||
coin_state: CoinState = coin_state_list[0]
|
||||
@ -1500,7 +1494,7 @@ class WalletRpcApi:
|
||||
# Find the unspent coin
|
||||
while coin_state.spent_height is not None:
|
||||
coin_state_list = await self.service.wallet_state_manager.wallet_node.fetch_children(
|
||||
peer, coin_state.coin.name()
|
||||
coin_state.coin.name()
|
||||
)
|
||||
odd_coin = 0
|
||||
for coin in coin_state_list:
|
||||
@ -1513,7 +1507,7 @@ class WalletRpcApi:
|
||||
coin_state = coin_state_list[0]
|
||||
# Get parent coin
|
||||
parent_coin_state_list: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[coin_state.coin.parent_coin_info], peer=peer
|
||||
[coin_state.coin.parent_coin_info]
|
||||
)
|
||||
if parent_coin_state_list is None or len(parent_coin_state_list) < 1:
|
||||
return {
|
||||
@ -1522,7 +1516,7 @@ class WalletRpcApi:
|
||||
}
|
||||
parent_coin_state: CoinState = parent_coin_state_list[0]
|
||||
coin_spend: CoinSpend = await self.service.wallet_state_manager.wallet_node.fetch_puzzle_solution(
|
||||
peer, parent_coin_state.spent_height, parent_coin_state.coin
|
||||
parent_coin_state.spent_height, parent_coin_state.coin
|
||||
)
|
||||
# convert to NFTInfo
|
||||
try:
|
||||
@ -1549,7 +1543,7 @@ class WalletRpcApi:
|
||||
|
||||
# Get launcher coin
|
||||
launcher_coin: List[CoinState] = await self.service.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[uncurried_nft.singleton_launcher_id], peer=peer
|
||||
[uncurried_nft.singleton_launcher_id]
|
||||
)
|
||||
if launcher_coin is None or len(launcher_coin) < 1 or launcher_coin[0].spent_height is None:
|
||||
return {
|
||||
@ -1570,7 +1564,7 @@ class WalletRpcApi:
|
||||
else:
|
||||
return {"success": True, "nft_info": nft_info}
|
||||
|
||||
async def nft_add_uri(self, request) -> Dict:
|
||||
async def nft_add_uri(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
# Note metadata updater can only add one uri for one field per spend.
|
||||
# If you want to add multiple uris for one field, you need to spend multiple times.
|
||||
@ -1595,7 +1589,7 @@ class WalletRpcApi:
|
||||
# Rate Limited Wallet
|
||||
##########################################################################################
|
||||
|
||||
async def rl_set_user_info(self, request):
|
||||
async def rl_set_user_info(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(int(request["wallet_id"]))
|
||||
rl_user = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
origin = request["origin"]
|
||||
@ -1610,8 +1604,8 @@ class WalletRpcApi:
|
||||
)
|
||||
return {}
|
||||
|
||||
async def send_clawback_transaction(self, request):
|
||||
wallet_id = int(request["wallet_id"])
|
||||
async def send_clawback_transaction(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: RLWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
|
||||
fee = int(request["fee"])
|
||||
@ -1625,7 +1619,7 @@ class WalletRpcApi:
|
||||
"transaction_id": tx.name,
|
||||
}
|
||||
|
||||
async def add_rate_limited_funds(self, request):
|
||||
async def add_rate_limited_funds(self, request) -> EndpointResult:
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: RLWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
puzzle_hash = wallet.rl_get_aggregation_puzzlehash(wallet.rl_info.rl_puzzle_hash)
|
||||
@ -1633,7 +1627,7 @@ class WalletRpcApi:
|
||||
await wallet.rl_add_funds(request["amount"], puzzle_hash, request["fee"])
|
||||
return {"status": "SUCCESS"}
|
||||
|
||||
async def get_farmed_amount(self, request):
|
||||
async def get_farmed_amount(self, request) -> EndpointResult:
|
||||
tx_records: List[TransactionRecord] = await self.service.wallet_state_manager.tx_store.get_farming_rewards()
|
||||
amount = 0
|
||||
pool_reward_amount = 0
|
||||
@ -1649,6 +1643,11 @@ class WalletRpcApi:
|
||||
continue
|
||||
pool_reward_amount += record.amount
|
||||
height = record.height_farmed(self.service.constants.GENESIS_CHALLENGE)
|
||||
# .get_farming_rewards() above queries for only confirmed records. This
|
||||
# could be hinted by making TransactionRecord generic but streamable can't
|
||||
# handle that presently. Existing code would have raised an exception
|
||||
# anyways if this were to fail and we already have an assert below.
|
||||
assert height is not None
|
||||
if record.type == TransactionType.FEE_REWARD:
|
||||
fee_amount += record.amount - calculate_base_farmer_reward(height)
|
||||
farmer_reward_amount += calculate_base_farmer_reward(height)
|
||||
@ -1665,7 +1664,7 @@ class WalletRpcApi:
|
||||
"last_height_farmed": last_height_farmed,
|
||||
}
|
||||
|
||||
async def create_signed_transaction(self, request, hold_lock=True) -> Dict:
|
||||
async def create_signed_transaction(self, request, hold_lock=True) -> EndpointResult:
|
||||
if "additions" not in request or len(request["additions"]) < 1:
|
||||
raise ValueError("Specify additions list")
|
||||
|
||||
@ -1761,7 +1760,7 @@ class WalletRpcApi:
|
||||
##########################################################################################
|
||||
# Pool Wallet
|
||||
##########################################################################################
|
||||
async def pw_join_pool(self, request) -> Dict:
|
||||
async def pw_join_pool(self, request) -> EndpointResult:
|
||||
fee = uint64(request.get("fee", 0))
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: PoolWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
@ -1789,7 +1788,7 @@ class WalletRpcApi:
|
||||
total_fee, tx, fee_tx = await wallet.join_pool(new_target_state, fee)
|
||||
return {"total_fee": total_fee, "transaction": tx, "fee_transaction": fee_tx}
|
||||
|
||||
async def pw_self_pool(self, request) -> Dict:
|
||||
async def pw_self_pool(self, request) -> EndpointResult:
|
||||
# Leaving a pool requires two state transitions.
|
||||
# First we transition to PoolSingletonState.LEAVING_POOL
|
||||
# Then we transition to FARMING_TO_POOL or SELF_POOLING
|
||||
@ -1806,7 +1805,7 @@ class WalletRpcApi:
|
||||
total_fee, tx, fee_tx = await wallet.self_pool(fee)
|
||||
return {"total_fee": total_fee, "transaction": tx, "fee_transaction": fee_tx}
|
||||
|
||||
async def pw_absorb_rewards(self, request) -> Dict:
|
||||
async def pw_absorb_rewards(self, request) -> EndpointResult:
|
||||
"""Perform a sweep of the p2_singleton rewards controlled by the pool wallet singleton"""
|
||||
if await self.service.wallet_state_manager.synced() is False:
|
||||
raise ValueError("Wallet needs to be fully synced before collecting rewards")
|
||||
@ -1822,7 +1821,7 @@ class WalletRpcApi:
|
||||
state: PoolWalletInfo = await wallet.get_current_state()
|
||||
return {"state": state.to_json_dict(), "transaction": transaction, "fee_transaction": fee_tx}
|
||||
|
||||
async def pw_status(self, request) -> Dict:
|
||||
async def pw_status(self, request) -> EndpointResult:
|
||||
"""Return the complete state of the Pool wallet with id `request["wallet_id"]`"""
|
||||
wallet_id = uint32(request["wallet_id"])
|
||||
wallet: PoolWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||
|
@ -16,7 +16,7 @@ from chia.util.default_root import DEFAULT_ROOT_PATH
|
||||
# See: https://bugs.python.org/issue29288
|
||||
"".encode("idna")
|
||||
|
||||
SERVICE_NAME = "full_node"
|
||||
SERVICE_NAME = "seeder"
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ def service_kwargs_for_full_node_crawler(
|
||||
|
||||
|
||||
def main():
|
||||
config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", "seeder")
|
||||
config = load_config_cli(DEFAULT_ROOT_PATH, "config.yaml", SERVICE_NAME)
|
||||
overrides = config["network_overrides"]["constants"][config["selected_network"]]
|
||||
updated_constants = DEFAULT_CONSTANTS.replace_str_to_bytes(**overrides)
|
||||
kwargs = service_kwargs_for_full_node_crawler(DEFAULT_ROOT_PATH, config, updated_constants)
|
||||
|
@ -53,8 +53,6 @@ class Service:
|
||||
rpc_info: Optional[Tuple[type, int]] = None,
|
||||
parse_cli_args=True,
|
||||
connect_to_daemon=True,
|
||||
running_new_process=True,
|
||||
service_name_prefix="",
|
||||
max_request_body_size: Optional[int] = None,
|
||||
override_capabilities: Optional[List[Tuple[uint16, str]]] = None,
|
||||
) -> None:
|
||||
@ -71,24 +69,13 @@ class Service:
|
||||
self._rpc_close_task: Optional[asyncio.Task] = None
|
||||
self._network_id: str = network_id
|
||||
self.max_request_body_size = max_request_body_size
|
||||
self._running_new_process = running_new_process
|
||||
|
||||
# when we start this service as a component of an existing process,
|
||||
# don't change its proctitle
|
||||
if running_new_process:
|
||||
proctitle_name = f"chia_{service_name_prefix}{service_name}"
|
||||
setproctitle(proctitle_name)
|
||||
|
||||
self._log = logging.getLogger(service_name)
|
||||
|
||||
if parse_cli_args:
|
||||
service_config = load_config_cli(root_path, "config.yaml", service_name)
|
||||
self.service_config = load_config_cli(root_path, "config.yaml", service_name)
|
||||
else:
|
||||
service_config = load_config(root_path, "config.yaml", service_name)
|
||||
|
||||
# only initialize logging once per process
|
||||
if running_new_process:
|
||||
initialize_logging(service_name, service_config["logging"], root_path)
|
||||
self.service_config = load_config(root_path, "config.yaml", service_name)
|
||||
|
||||
self._rpc_info = rpc_info
|
||||
private_ca_crt, private_ca_key = private_ssl_ca_paths(root_path, self.config)
|
||||
@ -96,7 +83,7 @@ class Service:
|
||||
inbound_rlp = self.config.get("inbound_rate_limit_percent")
|
||||
outbound_rlp = self.config.get("outbound_rate_limit_percent")
|
||||
if node_type == NodeType.WALLET:
|
||||
inbound_rlp = service_config.get("inbound_rate_limit_percent", inbound_rlp)
|
||||
inbound_rlp = self.service_config.get("inbound_rate_limit_percent", inbound_rlp)
|
||||
outbound_rlp = 60
|
||||
capabilities_to_use: List[Tuple[uint16, str]] = capabilities
|
||||
if override_capabilities is not None:
|
||||
@ -114,7 +101,7 @@ class Service:
|
||||
outbound_rlp,
|
||||
capabilities_to_use,
|
||||
root_path,
|
||||
service_config,
|
||||
self.service_config,
|
||||
(private_ca_crt, private_ca_key),
|
||||
(chia_ca_crt, chia_ca_key),
|
||||
name=f"{service_name}_server",
|
||||
@ -154,9 +141,6 @@ class Service:
|
||||
|
||||
self._did_start = True
|
||||
|
||||
if self._running_new_process:
|
||||
self._enable_signals()
|
||||
|
||||
await self._node._start(**kwargs)
|
||||
self._node._shut_down = False
|
||||
|
||||
@ -201,7 +185,12 @@ class Service:
|
||||
await self.start()
|
||||
await self.wait_closed()
|
||||
|
||||
def _enable_signals(self) -> None:
|
||||
async def setup_process_global_state(self) -> None:
|
||||
# Being async forces this to be run from within an active event loop as is
|
||||
# needed for the signal handler setup.
|
||||
proctitle_name = f"chia_{self._service_name}"
|
||||
setproctitle(proctitle_name)
|
||||
initialize_logging(self._service_name, self.service_config["logging"], self.root_path)
|
||||
|
||||
global main_pid
|
||||
main_pid = os.getpid()
|
||||
@ -288,6 +277,7 @@ class Service:
|
||||
|
||||
async def async_run_service(*args, **kwargs) -> None:
|
||||
service = Service(*args, **kwargs)
|
||||
await service.setup_process_global_state()
|
||||
return await service.run()
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from typing import Dict
|
||||
|
||||
from chia.rpc.full_node_rpc_api import FullNodeRpcApi
|
||||
from chia.rpc.rpc_server import Endpoint
|
||||
from chia.rpc.rpc_server import Endpoint, EndpointResult
|
||||
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
||||
from chia.util.bech32m import decode_puzzle_hash
|
||||
|
||||
@ -12,7 +12,7 @@ class SimulatorFullNodeRpcApi(FullNodeRpcApi):
|
||||
routes["/farm_tx_block"] = self.farm_tx_block
|
||||
return routes
|
||||
|
||||
async def farm_tx_block(self, _request: Dict[str, object]) -> Dict[str, object]:
|
||||
async def farm_tx_block(self, _request: Dict[str, object]) -> EndpointResult:
|
||||
request_address = str(_request["address"])
|
||||
ph = decode_puzzle_hash(request_address)
|
||||
req = FarmNewBlockProtocol(ph)
|
||||
|
@ -8,7 +8,7 @@ from blspy import AugSchemeMPL, G2Element
|
||||
from chia.consensus.default_constants import DEFAULT_CONSTANTS
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.util.streamable import Streamable, dataclass_from_dict, recurse_jsonify, streamable
|
||||
from chia.util.streamable import Streamable, streamable_from_dict, recurse_jsonify, streamable
|
||||
from chia.wallet.util.debug_spend_bundle import debug_spend_bundle
|
||||
|
||||
from .coin_spend import CoinSpend
|
||||
@ -95,7 +95,7 @@ class SpendBundle(Streamable):
|
||||
warnings.warn("`coin_solutions` is now `coin_spends` in `SpendBundle.from_json_dict`")
|
||||
else:
|
||||
raise ValueError("JSON contains both `coin_solutions` and `coin_spends`, just use `coin_spends`")
|
||||
return dataclass_from_dict(cls, json_dict)
|
||||
return streamable_from_dict(cls, json_dict)
|
||||
|
||||
def to_json_dict(self, include_legacy_keys: bool = True, exclude_modern_keys: bool = True):
|
||||
if include_legacy_keys is False and exclude_modern_keys is True:
|
||||
|
@ -50,7 +50,7 @@ def get_os_passphrase_store() -> Optional[OSPassphraseStore]:
|
||||
|
||||
|
||||
def check_legacy_keyring_keys_present(keyring: LegacyKeyring) -> bool:
|
||||
from keyring.credentials import SimpleCredential
|
||||
from keyring.credentials import Credential
|
||||
from chia.util.keychain import default_keychain_user, default_keychain_service, get_private_key_user, MAX_KEYS
|
||||
|
||||
keychain_user: str = default_keychain_user()
|
||||
@ -58,7 +58,7 @@ def check_legacy_keyring_keys_present(keyring: LegacyKeyring) -> bool:
|
||||
|
||||
for index in range(0, MAX_KEYS):
|
||||
current_user: str = get_private_key_user(keychain_user, index)
|
||||
credential: Optional[SimpleCredential] = keyring.get_credential(keychain_service, current_user)
|
||||
credential: Optional[Credential] = keyring.get_credential(keychain_service, current_user)
|
||||
if credential is not None:
|
||||
return True
|
||||
return False
|
||||
|
@ -169,7 +169,7 @@ def convert_primitive(f_type: Type[Any], item: Any) -> Any:
|
||||
raise TypeError(f"Can't convert type {type(item).__name__} to {f_type.__name__}: {e}") from e
|
||||
|
||||
|
||||
def dataclass_from_dict(klass: Type[Any], item: Any) -> Any:
|
||||
def streamable_from_dict(klass: Type[_T_Streamable], item: Any) -> _T_Streamable:
|
||||
"""
|
||||
Converts a dictionary based on a dataclass, into an instance of that dataclass.
|
||||
Recursively goes through lists, optionals, and dictionaries.
|
||||
@ -179,22 +179,12 @@ def dataclass_from_dict(klass: Type[Any], item: Any) -> Any:
|
||||
if not isinstance(item, dict):
|
||||
raise TypeError(f"expected: dict, actual: {type(item).__name__}")
|
||||
|
||||
if klass not in CONVERT_FUNCTIONS_FOR_STREAMABLE_CLASS:
|
||||
# For non-streamable dataclasses we can't populate the cache on startup, so we do it here for convert
|
||||
# functions only.
|
||||
fields = create_fields_cache(klass)
|
||||
convert_funcs = [function_to_convert_one_item(field.type) for field in fields]
|
||||
FIELDS_FOR_STREAMABLE_CLASS[klass] = fields
|
||||
CONVERT_FUNCTIONS_FOR_STREAMABLE_CLASS[klass] = convert_funcs
|
||||
else:
|
||||
fields = FIELDS_FOR_STREAMABLE_CLASS[klass]
|
||||
convert_funcs = CONVERT_FUNCTIONS_FOR_STREAMABLE_CLASS[klass]
|
||||
|
||||
fields = FIELDS_FOR_STREAMABLE_CLASS[klass]
|
||||
try:
|
||||
return klass(
|
||||
**{
|
||||
field.name: convert_func(item[field.name])
|
||||
for field, convert_func in zip(fields, convert_funcs)
|
||||
for field, convert_func in zip(fields, CONVERT_FUNCTIONS_FOR_STREAMABLE_CLASS[klass])
|
||||
if field.name in item
|
||||
}
|
||||
)
|
||||
@ -224,9 +214,6 @@ def function_to_convert_one_item(f_type: Type[Any]) -> ConvertFunctionType:
|
||||
convert_inner_func = function_to_convert_one_item(inner_type)
|
||||
# Ignoring for now as the proper solution isn't obvious
|
||||
return lambda items: convert_list(convert_inner_func, items) # type: ignore[arg-type]
|
||||
elif dataclasses.is_dataclass(f_type):
|
||||
# Type is a dataclass, data is a dictionary
|
||||
return lambda item: dataclass_from_dict(f_type, item)
|
||||
elif hasattr(f_type, "from_json_dict"):
|
||||
return lambda item: f_type.from_json_dict(item)
|
||||
elif issubclass(f_type, bytes):
|
||||
@ -645,7 +632,7 @@ class Streamable:
|
||||
stream_func(getattr(self, field.name), f)
|
||||
|
||||
def get_hash(self) -> bytes32:
|
||||
return bytes32(std_hash(bytes(self), skip_bytes_conversion=True))
|
||||
return std_hash(bytes(self), skip_bytes_conversion=True)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls: Any, blob: bytes) -> Any:
|
||||
@ -670,5 +657,5 @@ class Streamable:
|
||||
return ret
|
||||
|
||||
@classmethod
|
||||
def from_json_dict(cls: Any, json_dict: Dict[str, Any]) -> Any:
|
||||
return dataclass_from_dict(cls, json_dict)
|
||||
def from_json_dict(cls: Type[_T_Streamable], json_dict: Dict[str, Any]) -> _T_Streamable:
|
||||
return streamable_from_dict(cls, json_dict)
|
||||
|
@ -335,22 +335,15 @@ class CATWallet:
|
||||
lineage = await self.get_lineage_proof_for_coin(coin)
|
||||
|
||||
if lineage is None:
|
||||
for node_id, node in self.wallet_state_manager.wallet_node.server.all_connections.items():
|
||||
try:
|
||||
coin_state = await self.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[coin.parent_coin_info], None, node
|
||||
)
|
||||
# check for empty list and continue on to next node
|
||||
if not coin_state:
|
||||
continue
|
||||
assert coin_state[0].coin.name() == coin.parent_coin_info
|
||||
coin_spend = await self.wallet_state_manager.wallet_node.fetch_puzzle_solution(
|
||||
node, coin_state[0].spent_height, coin_state[0].coin
|
||||
)
|
||||
await self.puzzle_solution_received(coin_spend, parent_coin=coin_state[0].coin)
|
||||
break
|
||||
except Exception as e:
|
||||
self.log.debug(f"Exception: {e}, traceback: {traceback.format_exc()}")
|
||||
try:
|
||||
coin_state = await self.wallet_state_manager.wallet_node.get_coin_state([coin.parent_coin_info])
|
||||
assert coin_state[0].coin.name() == coin.parent_coin_info
|
||||
coin_spend = await self.wallet_state_manager.wallet_node.fetch_puzzle_solution(
|
||||
coin_state[0].spent_height, coin_state[0].coin
|
||||
)
|
||||
await self.puzzle_solution_received(coin_spend, parent_coin=coin_state[0].coin)
|
||||
except Exception as e:
|
||||
self.log.debug(f"Exception: {e}, traceback: {traceback.format_exc()}")
|
||||
|
||||
async def puzzle_solution_received(self, coin_spend: CoinSpend, parent_coin: Coin):
|
||||
coin_name = coin_spend.coin.name()
|
||||
|
@ -434,22 +434,21 @@ class DIDWallet:
|
||||
did_info.origin_coin.name(),
|
||||
did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
|
||||
)
|
||||
node = self.wallet_state_manager.wallet_node.get_full_node_peer()
|
||||
children = await self.wallet_state_manager.wallet_node.fetch_children(node, did_info.origin_coin.name())
|
||||
wallet_node = self.wallet_state_manager.wallet_node
|
||||
parent_coin: Coin = did_info.origin_coin
|
||||
while True:
|
||||
children = await wallet_node.fetch_children(parent_coin.name())
|
||||
if len(children) == 0:
|
||||
break
|
||||
|
||||
children_state: CoinState = children[0]
|
||||
coin = children_state.coin
|
||||
name = coin.name()
|
||||
children = await self.wallet_state_manager.wallet_node.fetch_children(node, name)
|
||||
child_coin = children_state.coin
|
||||
future_parent = LineageProof(
|
||||
coin.parent_coin_info,
|
||||
child_coin.parent_coin_info,
|
||||
did_info.current_inner.get_tree_hash(),
|
||||
uint64(coin.amount),
|
||||
uint64(child_coin.amount),
|
||||
)
|
||||
await self.add_parent(coin.name(), future_parent, True)
|
||||
await self.add_parent(child_coin.name(), future_parent, True)
|
||||
if children_state.spent_height != children_state.created_height:
|
||||
did_info = DIDInfo(
|
||||
did_info.origin_coin,
|
||||
@ -457,7 +456,7 @@ class DIDWallet:
|
||||
did_info.num_of_backup_ids_needed,
|
||||
self.did_info.parent_info,
|
||||
did_info.current_inner,
|
||||
coin,
|
||||
child_coin,
|
||||
new_did_inner_puzhash,
|
||||
new_pubkey,
|
||||
False,
|
||||
@ -466,23 +465,19 @@ class DIDWallet:
|
||||
|
||||
await self.save_info(did_info, True)
|
||||
assert children_state.created_height
|
||||
puzzle_solution_request = wallet_protocol.RequestPuzzleSolution(
|
||||
coin.parent_coin_info, children_state.created_height
|
||||
parent_spend = await wallet_node.fetch_puzzle_solution(children_state.created_height, parent_coin)
|
||||
assert parent_spend is not None
|
||||
parent_innerpuz = did_wallet_puzzles.get_innerpuzzle_from_puzzle(
|
||||
parent_spend.puzzle_reveal.to_program()
|
||||
)
|
||||
parent_state: CoinState = (
|
||||
await self.wallet_state_manager.wallet_node.get_coin_state([coin.parent_coin_info])
|
||||
)[0]
|
||||
response = await node.request_puzzle_solution(puzzle_solution_request)
|
||||
req_puz_sol = response.response
|
||||
assert req_puz_sol.puzzle is not None
|
||||
parent_innerpuz = did_wallet_puzzles.get_innerpuzzle_from_puzzle(req_puz_sol.puzzle.to_program())
|
||||
assert parent_innerpuz is not None
|
||||
parent_info = LineageProof(
|
||||
parent_state.coin.parent_coin_info,
|
||||
parent_coin.parent_coin_info,
|
||||
parent_innerpuz.get_tree_hash(),
|
||||
uint64(parent_state.coin.amount),
|
||||
uint64(parent_coin.amount),
|
||||
)
|
||||
await self.add_parent(coin.parent_coin_info, parent_info, True)
|
||||
await self.add_parent(child_coin.parent_coin_info, parent_info, True)
|
||||
parent_coin = child_coin
|
||||
assert parent_info is not None
|
||||
|
||||
async def create_tandem_xch_tx(
|
||||
|
@ -7,8 +7,6 @@ from typing import Any, Dict, List, Optional, Set, Tuple, Type, TypeVar
|
||||
from blspy import AugSchemeMPL, G2Element
|
||||
|
||||
from chia.protocols.wallet_protocol import CoinState
|
||||
from chia.server.outbound_message import NodeType
|
||||
from chia.server.ws_connection import WSChiaConnection
|
||||
from chia.types.announcement import Announcement
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.program import Program
|
||||
@ -158,22 +156,14 @@ class NFTWallet:
|
||||
if coin_info.coin == coin:
|
||||
return
|
||||
wallet_node = self.wallet_state_manager.wallet_node
|
||||
server = wallet_node.server
|
||||
full_nodes: Dict[bytes32, WSChiaConnection] = server.connection_by_type.get(NodeType.FULL_NODE, {})
|
||||
cs: Optional[CoinSpend] = None
|
||||
coin_states: Optional[List[CoinState]] = await self.wallet_state_manager.wallet_node.get_coin_state(
|
||||
[coin.parent_coin_info]
|
||||
)
|
||||
coin_states: Optional[List[CoinState]] = await wallet_node.get_coin_state([coin.parent_coin_info])
|
||||
if not coin_states:
|
||||
# farm coin
|
||||
return
|
||||
assert coin_states
|
||||
parent_coin = coin_states[0].coin
|
||||
for node_id in full_nodes:
|
||||
node = server.all_connections[node_id]
|
||||
cs = await wallet_node.fetch_puzzle_solution(node, height, parent_coin)
|
||||
if cs is not None:
|
||||
break
|
||||
cs = await wallet_node.fetch_puzzle_solution(height, parent_coin)
|
||||
assert cs is not None
|
||||
await self.puzzle_solution_received(cs, in_transaction=in_transaction)
|
||||
|
||||
|
@ -3,6 +3,7 @@ import logging
|
||||
import time
|
||||
import traceback
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||
from typing_extensions import Literal
|
||||
|
||||
from chia.data_layer.data_layer_wallet import DataLayerWallet
|
||||
from chia.protocols.wallet_protocol import CoinState
|
||||
@ -299,10 +300,12 @@ class TradeManager:
|
||||
solver: Solver = Solver({}),
|
||||
fee: uint64 = uint64(0),
|
||||
validate_only: bool = False,
|
||||
) -> Tuple[bool, Optional[TradeRecord], Optional[str]]:
|
||||
success, created_offer, error = await self._create_offer_for_ids(offer, driver_dict, solver, fee=fee)
|
||||
if not success or created_offer is None:
|
||||
raise Exception(f"Error creating offer: {error}")
|
||||
) -> Union[Tuple[Literal[True], TradeRecord, None], Tuple[Literal[False], None, str]]:
|
||||
result = await self._create_offer_for_ids(offer, driver_dict, solver, fee=fee)
|
||||
if not result[0] or result[1] is None:
|
||||
raise Exception(f"Error creating offer: {result[2]}")
|
||||
|
||||
success, created_offer, error = result
|
||||
|
||||
now = uint64(int(time.time()))
|
||||
trade_offer: TradeRecord = TradeRecord(
|
||||
@ -330,7 +333,7 @@ class TradeManager:
|
||||
driver_dict: Dict[bytes32, PuzzleInfo] = {},
|
||||
solver: Solver = Solver({}),
|
||||
fee: uint64 = uint64(0),
|
||||
) -> Tuple[bool, Optional[Offer], Optional[str]]:
|
||||
) -> Union[Tuple[Literal[True], Offer, None], Tuple[Literal[False], None, str]]:
|
||||
"""
|
||||
Offer is dictionary of wallet ids and amount
|
||||
"""
|
||||
@ -587,7 +590,7 @@ class TradeManager:
|
||||
offer: Offer,
|
||||
solver: Solver = Solver({}),
|
||||
fee=uint64(0),
|
||||
) -> Tuple[bool, Optional[TradeRecord], Optional[str]]:
|
||||
) -> Union[Tuple[Literal[True], TradeRecord, None], Tuple[Literal[False], None, str]]:
|
||||
take_offer_dict: Dict[Union[bytes32, int], int] = {}
|
||||
arbitrage: Dict[Optional[bytes32], int] = offer.arbitrage()
|
||||
|
||||
@ -610,11 +613,13 @@ class TradeManager:
|
||||
valid: bool = await self.check_offer_validity(offer)
|
||||
if not valid:
|
||||
return False, None, "This offer is no longer valid"
|
||||
success, take_offer, error = await self._create_offer_for_ids(
|
||||
result = await self._create_offer_for_ids(
|
||||
take_offer_dict, offer.driver_dict, solver, fee=fee
|
||||
)
|
||||
if not success or take_offer is None:
|
||||
return False, None, error
|
||||
if not result[0] or result[1] is None:
|
||||
return False, None, result[2]
|
||||
|
||||
success, take_offer, error = result
|
||||
|
||||
complete_offer = await self.check_for_final_modifications(Offer.aggregate([offer, take_offer]), solver)
|
||||
assert complete_offer.is_valid()
|
||||
|
@ -45,6 +45,8 @@ class TransactionRecord(Streamable):
|
||||
sent_to: List[Tuple[str, uint8, Optional[str]]]
|
||||
trade_id: Optional[bytes32]
|
||||
type: uint32 # TransactionType
|
||||
|
||||
# name is also called bundle_id and tx_id
|
||||
name: bytes32
|
||||
memos: List[Tuple[bytes32, List[bytes]]]
|
||||
|
||||
|
@ -2,8 +2,8 @@ import enum
|
||||
|
||||
|
||||
class SortKey(enum.Enum):
|
||||
CONFIRMED_AT_HEIGHT = "order by confirmed_at_height {ASC}"
|
||||
RELEVANCE = "order by confirmed {ASC}, confirmed_at_height {DESC}, created_at_time {DESC}"
|
||||
CONFIRMED_AT_HEIGHT = "ORDER BY confirmed_at_height {ASC}"
|
||||
RELEVANCE = "ORDER BY confirmed {ASC}, confirmed_at_height {DESC}, created_at_time {DESC}"
|
||||
|
||||
def ascending(self) -> str:
|
||||
return self.value.format(ASC="ASC", DESC="DESC")
|
||||
|
@ -26,9 +26,7 @@ from chia.protocols.full_node_protocol import RequestProofOfWeight, RespondProof
|
||||
from chia.protocols.protocol_message_types import ProtocolMessageTypes
|
||||
from chia.protocols.wallet_protocol import (
|
||||
CoinState,
|
||||
RequestSESInfo,
|
||||
RespondBlockHeader,
|
||||
RespondSESInfo,
|
||||
RespondToCoinUpdates,
|
||||
RespondToPhUpdates,
|
||||
)
|
||||
@ -44,7 +42,7 @@ from chia.types.coin_spend import CoinSpend
|
||||
from chia.types.header_block import HeaderBlock
|
||||
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.types.weight_proof import SubEpochData, WeightProof
|
||||
from chia.types.weight_proof import WeightProof
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.chunks import chunks
|
||||
from chia.util.config import WALLET_PEERS_PATH_KEY_DEPRECATED
|
||||
@ -514,7 +512,6 @@ class WalletNode:
|
||||
tb = traceback.format_exc()
|
||||
self.log.error(f"Exception while perform_atomic_rollback: {e} {tb}")
|
||||
await self.wallet_state_manager.db_wrapper.rollback_transaction()
|
||||
await self.wallet_state_manager.pool_store.rebuild_cache()
|
||||
raise
|
||||
else:
|
||||
await self.wallet_state_manager.blockchain.clean_block_records()
|
||||
@ -715,7 +712,6 @@ class WalletNode:
|
||||
tb = traceback.format_exc()
|
||||
self.log.error(f"Exception while adding state: {e} {tb}")
|
||||
await self.wallet_state_manager.db_wrapper.rollback_transaction()
|
||||
await self.wallet_state_manager.pool_store.rebuild_cache()
|
||||
else:
|
||||
await self.wallet_state_manager.blockchain.clean_block_records()
|
||||
|
||||
@ -750,7 +746,6 @@ class WalletNode:
|
||||
await self.wallet_state_manager.db_wrapper.commit_transaction()
|
||||
except Exception as e:
|
||||
await self.wallet_state_manager.db_wrapper.rollback_transaction()
|
||||
await self.wallet_state_manager.pool_store.rebuild_cache()
|
||||
tb = traceback.format_exc()
|
||||
self.log.error(f"Error adding states.. {e} {tb}")
|
||||
return False
|
||||
@ -834,13 +829,27 @@ class WalletNode:
|
||||
request.peak_hash,
|
||||
)
|
||||
|
||||
def get_full_node_peer(self) -> Optional[WSChiaConnection]:
|
||||
def get_full_node_peer(self, synced_only: bool = False) -> Optional[WSChiaConnection]:
|
||||
"""
|
||||
Get a full node, preferring synced & trusted > synced & untrusted > unsynced & trusted > unsynced & untrusted
|
||||
"""
|
||||
if self._server is None:
|
||||
return None
|
||||
|
||||
nodes = self.server.get_full_node_connections()
|
||||
if len(nodes) > 0:
|
||||
return random.choice(nodes)
|
||||
synced_peers = set(node for node in nodes if node.peer_node_id in self.synced_peers)
|
||||
trusted_peers = set(node for node in nodes if self.is_trusted(node))
|
||||
if len(synced_peers & trusted_peers) > 0:
|
||||
return random.choice(list(synced_peers & trusted_peers))
|
||||
elif len(synced_peers) > 0:
|
||||
return random.choice(list(synced_peers))
|
||||
elif synced_only:
|
||||
return None
|
||||
elif len(trusted_peers) > 0:
|
||||
return random.choice(list(trusted_peers))
|
||||
else:
|
||||
return random.choice(list(nodes))
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -1348,128 +1357,137 @@ class WalletNode:
|
||||
self.log.error("Failed validation 1")
|
||||
return False
|
||||
return True
|
||||
|
||||
# block is not included in wp recent chain
|
||||
start = block.height + 1
|
||||
compare_to_recent = False
|
||||
inserted: int = 0
|
||||
first_height_recent = weight_proof.recent_chain_data[0].height
|
||||
if start > first_height_recent - 1000:
|
||||
# compare up to weight_proof.recent_chain_data[0].height
|
||||
compare_to_recent = True
|
||||
end = first_height_recent
|
||||
else:
|
||||
start = block.height + 1
|
||||
compare_to_recent = False
|
||||
current_ses: Optional[SubEpochData] = None
|
||||
inserted: Optional[SubEpochData] = None
|
||||
first_height_recent = weight_proof.recent_chain_data[0].height
|
||||
if start > first_height_recent - 1000:
|
||||
compare_to_recent = True
|
||||
end = first_height_recent
|
||||
else:
|
||||
if block.height < self.constants.SUB_EPOCH_BLOCKS:
|
||||
inserted = weight_proof.sub_epochs[1]
|
||||
end = self.constants.SUB_EPOCH_BLOCKS + inserted.num_blocks_overflow
|
||||
else:
|
||||
request = RequestSESInfo(block.height, block.height + 32)
|
||||
res_ses: Optional[RespondSESInfo] = peer_request_cache.get_ses_request(block.height)
|
||||
if res_ses is None:
|
||||
res_ses = await peer.request_ses_hashes(request)
|
||||
peer_request_cache.add_to_ses_requests(block.height, res_ses)
|
||||
assert res_ses is not None
|
||||
|
||||
ses_0 = res_ses.reward_chain_hash[0]
|
||||
last_height = res_ses.heights[0][-1] # Last height in sub epoch
|
||||
end = last_height
|
||||
num_sub_epochs = len(weight_proof.sub_epochs)
|
||||
for idx, ses in enumerate(weight_proof.sub_epochs):
|
||||
if idx > num_sub_epochs - 3:
|
||||
break
|
||||
if ses.reward_chain_hash == ses_0:
|
||||
current_ses = ses
|
||||
inserted = weight_proof.sub_epochs[idx + 2]
|
||||
break
|
||||
if current_ses is None:
|
||||
self.log.error("Failed validation 2")
|
||||
return False
|
||||
|
||||
all_peers = self.server.get_full_node_connections()
|
||||
blocks: Optional[List[HeaderBlock]] = await fetch_header_blocks_in_range(
|
||||
start, end, peer_request_cache, all_peers
|
||||
)
|
||||
|
||||
if blocks is None:
|
||||
self.log.error(f"Error fetching blocks {start} {end}")
|
||||
return False
|
||||
|
||||
if compare_to_recent and weight_proof.recent_chain_data[0].header_hash != blocks[-1].header_hash:
|
||||
self.log.error("Failed validation 3")
|
||||
return False
|
||||
|
||||
if not compare_to_recent:
|
||||
last = blocks[-1].finished_sub_slots[-1].reward_chain.get_hash()
|
||||
if inserted is None or last != inserted.reward_chain_hash:
|
||||
self.log.error("Failed validation 4")
|
||||
return False
|
||||
pk_m_sig: List[Tuple[G1Element, bytes32, G2Element]] = []
|
||||
sigs_to_cache: List[HeaderBlock] = []
|
||||
blocks_to_cache: List[Tuple[bytes32, uint32]] = []
|
||||
|
||||
signatures_to_validate: int = 30
|
||||
for idx in range(len(blocks)):
|
||||
en_block = blocks[idx]
|
||||
if idx < signatures_to_validate and not peer_request_cache.in_block_signatures_validated(en_block):
|
||||
# Validate that the block is buried in the foliage by checking the signatures
|
||||
pk_m_sig.append(
|
||||
(
|
||||
en_block.reward_chain_block.proof_of_space.plot_public_key,
|
||||
en_block.foliage.foliage_block_data.get_hash(),
|
||||
en_block.foliage.foliage_block_data_signature,
|
||||
)
|
||||
)
|
||||
sigs_to_cache.append(en_block)
|
||||
|
||||
# This is the reward chain challenge. If this is in the cache, it means the prev block
|
||||
# has been validated. We must at least check the first block to ensure they are connected
|
||||
reward_chain_hash: bytes32 = en_block.reward_chain_block.reward_chain_ip_vdf.challenge
|
||||
if idx != 0 and peer_request_cache.in_blocks_validated(reward_chain_hash):
|
||||
# As soon as we see a block we have already concluded is in the chain, we can quit.
|
||||
if idx > signatures_to_validate:
|
||||
# get ses from wp
|
||||
start_height = block.height
|
||||
end_height = block.height + 32
|
||||
ses_start_height = 0
|
||||
end = 0
|
||||
for idx, ses in enumerate(weight_proof.sub_epochs):
|
||||
if idx == len(weight_proof.sub_epochs) - 1:
|
||||
break
|
||||
next_ses_height = (idx + 1) * self.constants.SUB_EPOCH_BLOCKS + weight_proof.sub_epochs[
|
||||
idx + 1
|
||||
].num_blocks_overflow
|
||||
# start_ses_hash
|
||||
if ses_start_height <= start_height < next_ses_height:
|
||||
inserted = idx + 1
|
||||
if ses_start_height < end_height < next_ses_height:
|
||||
end = next_ses_height
|
||||
break
|
||||
else:
|
||||
# Validate that the block is committed to by the weight proof
|
||||
if idx == 0:
|
||||
prev_block_rc_hash: bytes32 = block.reward_chain_block.get_hash()
|
||||
prev_hash = block.header_hash
|
||||
else:
|
||||
prev_block_rc_hash = blocks[idx - 1].reward_chain_block.get_hash()
|
||||
prev_hash = blocks[idx - 1].header_hash
|
||||
if idx > len(weight_proof.sub_epochs) - 3:
|
||||
break
|
||||
# else add extra ses as request start <-> end spans two ses
|
||||
end = (idx + 2) * self.constants.SUB_EPOCH_BLOCKS + weight_proof.sub_epochs[
|
||||
idx + 2
|
||||
].num_blocks_overflow
|
||||
inserted += 1
|
||||
break
|
||||
ses_start_height = next_ses_height
|
||||
|
||||
if not en_block.prev_header_hash == prev_hash:
|
||||
self.log.error("Failed validation 5")
|
||||
return False
|
||||
if end == 0:
|
||||
self.log.error("Error finding sub epoch")
|
||||
return False
|
||||
all_peers = self.server.get_full_node_connections()
|
||||
blocks: Optional[List[HeaderBlock]] = await fetch_header_blocks_in_range(
|
||||
start, end, peer_request_cache, all_peers
|
||||
)
|
||||
if blocks is None:
|
||||
self.log.error(f"Error fetching blocks {start} {end}")
|
||||
return False
|
||||
|
||||
if len(en_block.finished_sub_slots) > 0:
|
||||
reversed_slots = en_block.finished_sub_slots.copy()
|
||||
reversed_slots.reverse()
|
||||
for slot_idx, slot in enumerate(reversed_slots[:-1]):
|
||||
hash_val = reversed_slots[slot_idx + 1].reward_chain.get_hash()
|
||||
if not hash_val == slot.reward_chain.end_of_slot_vdf.challenge:
|
||||
self.log.error("Failed validation 6")
|
||||
return False
|
||||
if not prev_block_rc_hash == reversed_slots[-1].reward_chain.end_of_slot_vdf.challenge:
|
||||
self.log.error("Failed validation 7")
|
||||
return False
|
||||
else:
|
||||
if not prev_block_rc_hash == reward_chain_hash:
|
||||
self.log.error("Failed validation 8")
|
||||
return False
|
||||
blocks_to_cache.append((reward_chain_hash, en_block.height))
|
||||
if compare_to_recent and weight_proof.recent_chain_data[0].header_hash != blocks[-1].header_hash:
|
||||
self.log.error("Failed validation 3")
|
||||
return False
|
||||
|
||||
agg_sig: G2Element = AugSchemeMPL.aggregate([sig for (_, _, sig) in pk_m_sig])
|
||||
if not AugSchemeMPL.aggregate_verify(
|
||||
[pk for (pk, _, _) in pk_m_sig], [m for (_, m, _) in pk_m_sig], agg_sig
|
||||
):
|
||||
self.log.error("Failed signature validation")
|
||||
if not compare_to_recent:
|
||||
last = blocks[-1].finished_sub_slots[-1].reward_chain.get_hash()
|
||||
if last != weight_proof.sub_epochs[inserted].reward_chain_hash:
|
||||
self.log.error("Failed validation 4")
|
||||
return False
|
||||
for header_block in sigs_to_cache:
|
||||
peer_request_cache.add_to_block_signatures_validated(header_block)
|
||||
for reward_chain_hash, height in blocks_to_cache:
|
||||
peer_request_cache.add_to_blocks_validated(reward_chain_hash, height)
|
||||
return True
|
||||
pk_m_sig: List[Tuple[G1Element, bytes32, G2Element]] = []
|
||||
sigs_to_cache: List[HeaderBlock] = []
|
||||
blocks_to_cache: List[Tuple[bytes32, uint32]] = []
|
||||
|
||||
async def fetch_puzzle_solution(self, peer: WSChiaConnection, height: uint32, coin: Coin) -> CoinSpend:
|
||||
signatures_to_validate: int = 30
|
||||
for idx in range(len(blocks)):
|
||||
en_block = blocks[idx]
|
||||
if idx < signatures_to_validate and not peer_request_cache.in_block_signatures_validated(en_block):
|
||||
# Validate that the block is buried in the foliage by checking the signatures
|
||||
pk_m_sig.append(
|
||||
(
|
||||
en_block.reward_chain_block.proof_of_space.plot_public_key,
|
||||
en_block.foliage.foliage_block_data.get_hash(),
|
||||
en_block.foliage.foliage_block_data_signature,
|
||||
)
|
||||
)
|
||||
sigs_to_cache.append(en_block)
|
||||
|
||||
# This is the reward chain challenge. If this is in the cache, it means the prev block
|
||||
# has been validated. We must at least check the first block to ensure they are connected
|
||||
reward_chain_hash: bytes32 = en_block.reward_chain_block.reward_chain_ip_vdf.challenge
|
||||
if idx != 0 and peer_request_cache.in_blocks_validated(reward_chain_hash):
|
||||
# As soon as we see a block we have already concluded is in the chain, we can quit.
|
||||
if idx > signatures_to_validate:
|
||||
break
|
||||
else:
|
||||
# Validate that the block is committed to by the weight proof
|
||||
if idx == 0:
|
||||
prev_block_rc_hash: bytes32 = block.reward_chain_block.get_hash()
|
||||
prev_hash = block.header_hash
|
||||
else:
|
||||
prev_block_rc_hash = blocks[idx - 1].reward_chain_block.get_hash()
|
||||
prev_hash = blocks[idx - 1].header_hash
|
||||
|
||||
if not en_block.prev_header_hash == prev_hash:
|
||||
self.log.error("Failed validation 5")
|
||||
return False
|
||||
|
||||
if len(en_block.finished_sub_slots) > 0:
|
||||
reversed_slots = en_block.finished_sub_slots.copy()
|
||||
reversed_slots.reverse()
|
||||
for slot_idx, slot in enumerate(reversed_slots[:-1]):
|
||||
hash_val = reversed_slots[slot_idx + 1].reward_chain.get_hash()
|
||||
if not hash_val == slot.reward_chain.end_of_slot_vdf.challenge:
|
||||
self.log.error("Failed validation 6")
|
||||
return False
|
||||
if not prev_block_rc_hash == reversed_slots[-1].reward_chain.end_of_slot_vdf.challenge:
|
||||
self.log.error("Failed validation 7")
|
||||
return False
|
||||
else:
|
||||
if not prev_block_rc_hash == reward_chain_hash:
|
||||
self.log.error("Failed validation 8")
|
||||
return False
|
||||
blocks_to_cache.append((reward_chain_hash, en_block.height))
|
||||
|
||||
agg_sig: G2Element = AugSchemeMPL.aggregate([sig for (_, _, sig) in pk_m_sig])
|
||||
if not AugSchemeMPL.aggregate_verify([pk for (pk, _, _) in pk_m_sig], [m for (_, m, _) in pk_m_sig], agg_sig):
|
||||
self.log.error("Failed signature validation")
|
||||
return False
|
||||
for header_block in sigs_to_cache:
|
||||
peer_request_cache.add_to_block_signatures_validated(header_block)
|
||||
for reward_chain_hash, height in blocks_to_cache:
|
||||
peer_request_cache.add_to_blocks_validated(reward_chain_hash, height)
|
||||
return True
|
||||
|
||||
async def fetch_puzzle_solution(
|
||||
self, height: uint32, coin: Coin, peer: Optional[WSChiaConnection] = None
|
||||
) -> CoinSpend:
|
||||
if peer is None:
|
||||
peer = self.get_full_node_peer()
|
||||
if peer is None:
|
||||
raise ValueError("Could not find any peers to request puzzle and solution from")
|
||||
solution_response = await peer.request_puzzle_solution(
|
||||
wallet_protocol.RequestPuzzleSolution(coin.name(), height)
|
||||
)
|
||||
@ -1487,23 +1505,11 @@ class WalletNode:
|
||||
async def get_coin_state(
|
||||
self, coin_names: List[bytes32], fork_height: Optional[uint32] = None, peer: Optional[WSChiaConnection] = None
|
||||
) -> List[CoinState]:
|
||||
all_nodes = self.server.connection_by_type[NodeType.FULL_NODE]
|
||||
if len(all_nodes.keys()) == 0:
|
||||
raise ValueError("Not connected to the full node")
|
||||
# Use supplied if provided, prioritize trusted otherwise
|
||||
synced_peers = [node for node in all_nodes.values() if node.peer_node_id in self.synced_peers]
|
||||
if peer is None:
|
||||
for node in synced_peers:
|
||||
if self.is_trusted(node):
|
||||
peer = node
|
||||
break
|
||||
if peer is None:
|
||||
if len(synced_peers) > 0:
|
||||
peer = synced_peers[0]
|
||||
else:
|
||||
peer = list(all_nodes.values())[0]
|
||||
peer = self.get_full_node_peer()
|
||||
if peer is None:
|
||||
raise ValueError("Could not find any peers to request puzzle and solution from")
|
||||
|
||||
assert peer is not None
|
||||
msg = wallet_protocol.RegisterForCoinUpdates(coin_names, uint32(0))
|
||||
coin_state: Optional[RespondToCoinUpdates] = await peer.register_interest_in_coin(msg)
|
||||
assert coin_state is not None
|
||||
@ -1521,8 +1527,12 @@ class WalletNode:
|
||||
return coin_state.coin_states
|
||||
|
||||
async def fetch_children(
|
||||
self, peer: WSChiaConnection, coin_name: bytes32, fork_height: Optional[uint32] = None
|
||||
self, coin_name: bytes32, fork_height: Optional[uint32] = None, peer: Optional[WSChiaConnection] = None
|
||||
) -> List[CoinState]:
|
||||
if peer is None:
|
||||
peer = self.get_full_node_peer()
|
||||
if peer is None:
|
||||
raise ValueError("Could not find any peers to request puzzle and solution from")
|
||||
response: Optional[wallet_protocol.RespondChildren] = await peer.request_children(
|
||||
wallet_protocol.RequestChildren(coin_name)
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import logging
|
||||
from typing import List, Tuple, Dict, Optional
|
||||
from typing import List, Tuple
|
||||
|
||||
import aiosqlite
|
||||
|
||||
@ -13,7 +13,6 @@ log = logging.getLogger(__name__)
|
||||
class WalletPoolStore:
|
||||
db_connection: aiosqlite.Connection
|
||||
db_wrapper: DBWrapper
|
||||
_state_transitions_cache: Dict[int, List[Tuple[uint32, CoinSpend]]]
|
||||
|
||||
@classmethod
|
||||
async def create(cls, wrapper: DBWrapper):
|
||||
@ -23,11 +22,15 @@ class WalletPoolStore:
|
||||
self.db_wrapper = wrapper
|
||||
|
||||
await self.db_connection.execute(
|
||||
"CREATE TABLE IF NOT EXISTS pool_state_transitions(transition_index integer, wallet_id integer, "
|
||||
"height bigint, coin_spend blob, PRIMARY KEY(transition_index, wallet_id))"
|
||||
"CREATE TABLE IF NOT EXISTS pool_state_transitions("
|
||||
" transition_index integer,"
|
||||
" wallet_id integer,"
|
||||
" height bigint,"
|
||||
" coin_spend blob,"
|
||||
" PRIMARY KEY(transition_index, wallet_id))"
|
||||
)
|
||||
|
||||
await self.db_connection.commit()
|
||||
await self.rebuild_cache()
|
||||
return self
|
||||
|
||||
async def _clear_database(self):
|
||||
@ -51,28 +54,48 @@ class WalletPoolStore:
|
||||
if not in_transaction:
|
||||
await self.db_wrapper.lock.acquire()
|
||||
try:
|
||||
if wallet_id not in self._state_transitions_cache:
|
||||
self._state_transitions_cache[wallet_id] = []
|
||||
all_state_transitions: List[Tuple[uint32, CoinSpend]] = self.get_spends_for_wallet(wallet_id)
|
||||
# find the most recent transition in wallet_id
|
||||
rows = list(
|
||||
await self.db_connection.execute_fetchall(
|
||||
"SELECT transition_index, height, coin_spend "
|
||||
"FROM pool_state_transitions "
|
||||
"WHERE wallet_id=? "
|
||||
"ORDER BY transition_index DESC "
|
||||
"LIMIT 1",
|
||||
(wallet_id,),
|
||||
)
|
||||
)
|
||||
serialized_spend = bytes(spend)
|
||||
if len(rows) == 0:
|
||||
transition_index = 0
|
||||
else:
|
||||
existing = list(
|
||||
await self.db_connection.execute_fetchall(
|
||||
"SELECT COUNT(*) "
|
||||
"FROM pool_state_transitions "
|
||||
"WHERE wallet_id=? AND height=? AND coin_spend=?",
|
||||
(wallet_id, height, serialized_spend),
|
||||
)
|
||||
)
|
||||
if existing[0][0] != 0:
|
||||
# we already have this transition in the DB
|
||||
return
|
||||
|
||||
if (height, spend) in all_state_transitions:
|
||||
return
|
||||
|
||||
if len(all_state_transitions) > 0:
|
||||
if height < all_state_transitions[-1][0]:
|
||||
row = rows[0]
|
||||
if height < row[1]:
|
||||
raise ValueError("Height cannot go down")
|
||||
if spend.coin.parent_coin_info != all_state_transitions[-1][1].coin.name():
|
||||
prev = CoinSpend.from_bytes(row[2])
|
||||
if spend.coin.parent_coin_info != prev.coin.name():
|
||||
raise ValueError("New spend does not extend")
|
||||
|
||||
all_state_transitions.append((height, spend))
|
||||
transition_index = row[0]
|
||||
|
||||
cursor = await self.db_connection.execute(
|
||||
"INSERT OR REPLACE INTO pool_state_transitions VALUES (?, ?, ?, ?)",
|
||||
"INSERT OR IGNORE INTO pool_state_transitions VALUES (?, ?, ?, ?)",
|
||||
(
|
||||
len(all_state_transitions) - 1,
|
||||
transition_index + 1,
|
||||
wallet_id,
|
||||
height,
|
||||
bytes(spend),
|
||||
serialized_spend,
|
||||
),
|
||||
)
|
||||
await cursor.close()
|
||||
@ -81,27 +104,16 @@ class WalletPoolStore:
|
||||
await self.db_connection.commit()
|
||||
self.db_wrapper.lock.release()
|
||||
|
||||
def get_spends_for_wallet(self, wallet_id: int) -> List[Tuple[uint32, CoinSpend]]:
|
||||
async def get_spends_for_wallet(self, wallet_id: int) -> List[Tuple[uint32, CoinSpend]]:
|
||||
"""
|
||||
Retrieves all entries for a wallet ID from the cache, works even if commit is not called yet.
|
||||
Retrieves all entries for a wallet ID.
|
||||
"""
|
||||
return self._state_transitions_cache.get(wallet_id, [])
|
||||
|
||||
async def rebuild_cache(self) -> None:
|
||||
"""
|
||||
This resets the cache, and loads all entries from the DB. Any entries in the cache that were not committed
|
||||
are removed. This can happen if a state transition in wallet_blockchain fails.
|
||||
"""
|
||||
cursor = await self.db_connection.execute("SELECT * FROM pool_state_transitions ORDER BY transition_index")
|
||||
rows = await cursor.fetchall()
|
||||
await cursor.close()
|
||||
self._state_transitions_cache = {}
|
||||
for row in rows:
|
||||
_, wallet_id, height, coin_spend_bytes = row
|
||||
coin_spend: CoinSpend = CoinSpend.from_bytes(coin_spend_bytes)
|
||||
if wallet_id not in self._state_transitions_cache:
|
||||
self._state_transitions_cache[wallet_id] = []
|
||||
self._state_transitions_cache[wallet_id].append((height, coin_spend))
|
||||
rows = await self.db_connection.execute_fetchall(
|
||||
"SELECT height, coin_spend FROM pool_state_transitions WHERE wallet_id=? ORDER BY transition_index",
|
||||
(wallet_id,),
|
||||
)
|
||||
return [(uint32(row[0]), CoinSpend.from_bytes(row[1])) for row in rows]
|
||||
|
||||
async def rollback(self, height: int, wallet_id_arg: int, in_transaction: bool) -> None:
|
||||
"""
|
||||
@ -113,14 +125,6 @@ class WalletPoolStore:
|
||||
if not in_transaction:
|
||||
await self.db_wrapper.lock.acquire()
|
||||
try:
|
||||
for wallet_id, items in self._state_transitions_cache.items():
|
||||
remove_index_start: Optional[int] = None
|
||||
for i, (item_block_height, _) in enumerate(items):
|
||||
if item_block_height > height and wallet_id == wallet_id_arg:
|
||||
remove_index_start = i
|
||||
break
|
||||
if remove_index_start is not None:
|
||||
del items[remove_index_start:]
|
||||
cursor = await self.db_connection.execute(
|
||||
"DELETE FROM pool_state_transitions WHERE height>? AND wallet_id=?", (height, wallet_id_arg)
|
||||
)
|
||||
|
@ -119,7 +119,6 @@ class WalletStateManager:
|
||||
blockchain: WalletBlockchain
|
||||
coin_store: WalletCoinStore
|
||||
sync_store: WalletSyncStore
|
||||
finished_sync_up_to: uint32
|
||||
interested_store: WalletInterestedStore
|
||||
multiprocessing_context: multiprocessing.context.BaseContext
|
||||
weight_proof_handler: WalletWeightProofHandler
|
||||
@ -187,7 +186,6 @@ class WalletStateManager:
|
||||
self.wallet_node = wallet_node
|
||||
self.sync_mode = False
|
||||
self.sync_target = uint32(0)
|
||||
self.finished_sync_up_to = uint32(0)
|
||||
multiprocessing_start_method = process_config_start_method(config=self.config, log=self.log)
|
||||
self.multiprocessing_context = multiprocessing.get_context(method=multiprocessing_start_method)
|
||||
self.weight_proof_handler = WalletWeightProofHandler(
|
||||
@ -604,7 +602,7 @@ class WalletStateManager:
|
||||
assert parent_coin_state.spent_height == coin_state.created_height
|
||||
|
||||
coin_spend: Optional[CoinSpend] = await self.wallet_node.fetch_puzzle_solution(
|
||||
peer, parent_coin_state.spent_height, parent_coin_state.coin
|
||||
parent_coin_state.spent_height, parent_coin_state.coin, peer
|
||||
)
|
||||
if coin_spend is None:
|
||||
return None, None
|
||||
@ -991,7 +989,7 @@ class WalletStateManager:
|
||||
)
|
||||
await self.tx_store.add_transaction_record(tx_record, True)
|
||||
|
||||
children = await self.wallet_node.fetch_children(peer, coin_name, fork_height)
|
||||
children = await self.wallet_node.fetch_children(coin_name, fork_height, peer)
|
||||
assert children is not None
|
||||
additions = [state.coin for state in children]
|
||||
if len(children) > 0:
|
||||
@ -1068,7 +1066,7 @@ class WalletStateManager:
|
||||
|
||||
while curr_coin_state.spent_height is not None:
|
||||
cs: CoinSpend = await self.wallet_node.fetch_puzzle_solution(
|
||||
peer, curr_coin_state.spent_height, curr_coin_state.coin
|
||||
curr_coin_state.spent_height, curr_coin_state.coin, peer
|
||||
)
|
||||
success = await wallet.apply_state_transition(cs, curr_coin_state.spent_height)
|
||||
if not success:
|
||||
@ -1093,7 +1091,7 @@ class WalletStateManager:
|
||||
curr_coin_state = new_coin_state[0]
|
||||
if record.wallet_type == WalletType.DATA_LAYER:
|
||||
singleton_spend = await self.wallet_node.fetch_puzzle_solution(
|
||||
peer, coin_state.spent_height, coin_state.coin
|
||||
coin_state.spent_height, coin_state.coin, peer
|
||||
)
|
||||
dl_wallet = self.wallets[uint32(record.wallet_id)]
|
||||
await dl_wallet.singleton_removed(
|
||||
@ -1109,7 +1107,7 @@ class WalletStateManager:
|
||||
|
||||
# Check if a child is a singleton launcher
|
||||
if children is None:
|
||||
children = await self.wallet_node.fetch_children(peer, coin_name, fork_height)
|
||||
children = await self.wallet_node.fetch_children(coin_name, fork_height, peer)
|
||||
assert children is not None
|
||||
for child in children:
|
||||
if child.coin.puzzle_hash != SINGLETON_LAUNCHER_HASH:
|
||||
@ -1120,7 +1118,7 @@ class WalletStateManager:
|
||||
# TODO handle spending launcher later block
|
||||
continue
|
||||
launcher_spend: Optional[CoinSpend] = await self.wallet_node.fetch_puzzle_solution(
|
||||
peer, coin_state.spent_height, child.coin
|
||||
coin_state.spent_height, child.coin, peer
|
||||
)
|
||||
if launcher_spend is None:
|
||||
continue
|
||||
|
@ -210,7 +210,7 @@ class WalletTransactionStore:
|
||||
await self.add_transaction_record(tx, False)
|
||||
return True
|
||||
|
||||
async def tx_reorged(self, record: TransactionRecord, in_transaction: bool):
|
||||
async def tx_reorged(self, record: TransactionRecord, in_transaction: bool) -> None:
|
||||
"""
|
||||
Updates transaction sent count to 0 and resets confirmation data
|
||||
"""
|
||||
@ -246,14 +246,17 @@ class WalletTransactionStore:
|
||||
return TransactionRecord.from_bytes(rows[0][0])
|
||||
return None
|
||||
|
||||
# TODO: This should probably be split into separate function, one that
|
||||
# queries the state and one that updates it. Also, include_accepted_txs=True
|
||||
# might be a separate function too.
|
||||
# also, the current time should be passed in as a paramter
|
||||
async def get_not_sent(self, *, include_accepted_txs=False) -> List[TransactionRecord]:
|
||||
"""
|
||||
Returns the list of transactions that have not been received by full node yet.
|
||||
"""
|
||||
current_time = int(time.time())
|
||||
rows = await self.db_connection.execute_fetchall(
|
||||
"SELECT * from transaction_record WHERE confirmed=?",
|
||||
(0,),
|
||||
"SELECT * from transaction_record WHERE confirmed=0",
|
||||
)
|
||||
records = []
|
||||
|
||||
@ -287,29 +290,17 @@ class WalletTransactionStore:
|
||||
fee_int = TransactionType.FEE_REWARD.value
|
||||
pool_int = TransactionType.COINBASE_REWARD.value
|
||||
rows = await self.db_connection.execute_fetchall(
|
||||
"SELECT * from transaction_record WHERE confirmed=? and (type=? or type=?)", (1, fee_int, pool_int)
|
||||
"SELECT * from transaction_record WHERE confirmed=1 and (type=? or type=?)", (fee_int, pool_int)
|
||||
)
|
||||
records = []
|
||||
|
||||
for row in rows:
|
||||
record = TransactionRecord.from_bytes(row[0])
|
||||
records.append(record)
|
||||
|
||||
return records
|
||||
return [TransactionRecord.from_bytes(row[0]) for row in rows]
|
||||
|
||||
async def get_all_unconfirmed(self) -> List[TransactionRecord]:
|
||||
"""
|
||||
Returns the list of all transaction that have not yet been confirmed.
|
||||
"""
|
||||
|
||||
rows = await self.db_connection.execute_fetchall("SELECT * from transaction_record WHERE confirmed=?", (0,))
|
||||
records = []
|
||||
|
||||
for row in rows:
|
||||
record = TransactionRecord.from_bytes(row[0])
|
||||
records.append(record)
|
||||
|
||||
return records
|
||||
rows = await self.db_connection.execute_fetchall("SELECT * from transaction_record WHERE confirmed=0")
|
||||
return [TransactionRecord.from_bytes(row[0]) for row in rows]
|
||||
|
||||
async def get_unconfirmed_for_wallet(self, wallet_id: int) -> List[TransactionRecord]:
|
||||
"""
|
||||
@ -331,7 +322,7 @@ class WalletTransactionStore:
|
||||
if to_puzzle_hash is None:
|
||||
puzz_hash_where = ""
|
||||
else:
|
||||
puzz_hash_where = f' and to_puzzle_hash="{to_puzzle_hash.hex()}"'
|
||||
puzz_hash_where = f' AND to_puzzle_hash="{to_puzzle_hash.hex()}"'
|
||||
|
||||
if sort_key is None:
|
||||
sort_key = "CONFIRMED_AT_HEIGHT"
|
||||
@ -344,7 +335,7 @@ class WalletTransactionStore:
|
||||
query_str = SortKey[sort_key].ascending()
|
||||
|
||||
rows = await self.db_connection.execute_fetchall(
|
||||
f"SELECT * from transaction_record where wallet_id=?{puzz_hash_where}"
|
||||
f"SELECT * from transaction_record WHERE wallet_id=?{puzz_hash_where}"
|
||||
f" {query_str}, rowid"
|
||||
f" LIMIT {start}, {limit}",
|
||||
(wallet_id,),
|
||||
@ -366,11 +357,11 @@ class WalletTransactionStore:
|
||||
"""
|
||||
if type is None:
|
||||
rows = await self.db_connection.execute_fetchall(
|
||||
"SELECT * from transaction_record where wallet_id=?", (wallet_id,)
|
||||
"SELECT * FROM transaction_record WHERE wallet_id=?", (wallet_id,)
|
||||
)
|
||||
else:
|
||||
rows = await self.db_connection.execute_fetchall(
|
||||
"SELECT * from transaction_record where wallet_id=? and type=?",
|
||||
"SELECT * FROM transaction_record WHERE wallet_id=? AND type=?",
|
||||
(
|
||||
wallet_id,
|
||||
type,
|
||||
|
12
setup.py
12
setup.py
@ -13,13 +13,13 @@ dependencies = [
|
||||
"aiohttp==3.8.1", # HTTP server for full node rpc
|
||||
"aiosqlite==0.17.0", # asyncio wrapper for sqlite, to store blocks
|
||||
"bitstring==3.1.9", # Binary data management library
|
||||
"colorama==0.4.4", # Colorizes terminal output
|
||||
"colorama==0.4.5", # Colorizes terminal output
|
||||
"colorlog==6.6.0", # Adds color to logs
|
||||
"concurrent-log-handler==0.9.19", # Concurrently log and rotate logs
|
||||
"cryptography==36.0.2", # Python cryptography library for TLS - keyring conflict
|
||||
"fasteners==0.16.3", # For interprocess file locking, expected to be replaced by filelock
|
||||
"filelock==3.4.2", # For reading and writing config multiprocess and multithread safely (non-reentrant locks)
|
||||
"keyring==23.0.1", # Store keys in MacOS Keychain, Windows Credential Locker
|
||||
"fasteners==0.17.3", # For interprocess file locking, expected to be replaced by filelock
|
||||
"filelock==3.7.1", # For reading and writing config multiprocess and multithread safely (non-reentrant locks)
|
||||
"keyring==23.6.0", # Store keys in MacOS Keychain, Windows Credential Locker
|
||||
"keyrings.cryptfile==1.3.4", # Secure storage for keys on Linux (Will be replaced)
|
||||
# "keyrings.cryptfile==1.3.8", # Secure storage for keys on Linux (Will be replaced)
|
||||
# See https://github.com/frispete/keyrings.cryptfile/issues/15
|
||||
@ -31,9 +31,9 @@ dependencies = [
|
||||
"dnspython==2.2.0", # Query DNS seeds
|
||||
"watchdog==2.1.9", # Filesystem event watching - watches keyring.yaml
|
||||
"dnslib==0.9.17", # dns lib
|
||||
"typing-extensions==4.0.1", # typing backports like Protocol and TypedDict
|
||||
"typing-extensions==4.3.0", # typing backports like Protocol and TypedDict
|
||||
"zstd==1.5.0.4",
|
||||
"packaging==21.0",
|
||||
"packaging==21.3",
|
||||
]
|
||||
|
||||
upnp_dependencies = [
|
||||
|
@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
from pathlib import Path
|
||||
from typing import AsyncIterator, Dict, List, Tuple, Set
|
||||
import pytest
|
||||
@ -32,6 +33,7 @@ nodes = Tuple[WalletNode, FullNodeSimulator]
|
||||
nodes_with_port = Tuple[WalletNode, FullNodeSimulator, int]
|
||||
|
||||
|
||||
@contextlib.asynccontextmanager
|
||||
async def init_data_layer(wallet_rpc_port: int, bt: BlockTools, db_path: Path) -> AsyncIterator[DataLayer]:
|
||||
config = bt.config
|
||||
config["data_layer"]["wallet_peer"]["port"] = wallet_rpc_port
|
||||
@ -43,11 +45,13 @@ async def init_data_layer(wallet_rpc_port: int, bt: BlockTools, db_path: Path) -
|
||||
save_config(bt.root_path, "config.yaml", config)
|
||||
kwargs = service_kwargs_for_data_layer(root_path=bt.root_path, config=config)
|
||||
kwargs.update(parse_cli_args=False)
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
await service.start()
|
||||
yield service._api.data_layer
|
||||
service.stop()
|
||||
await service.wait_closed()
|
||||
try:
|
||||
yield service._api.data_layer
|
||||
finally:
|
||||
service.stop()
|
||||
await service.wait_closed()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="function")
|
||||
@ -94,7 +98,7 @@ async def test_create_insert_get(one_wallet_node_and_rpc: nodes_with_port, bt: B
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
# test insert
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
key = b"a"
|
||||
@ -162,7 +166,7 @@ async def test_upsert(one_wallet_node_and_rpc: nodes_with_port, bt: BlockTools,
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
# test insert
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
key = b"a"
|
||||
@ -207,7 +211,7 @@ async def test_create_double_insert(one_wallet_node_and_rpc: nodes_with_port, bt
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
assert res is not None
|
||||
@ -269,7 +273,7 @@ async def test_keys_values_ancestors(one_wallet_node_and_rpc: nodes_with_port, b
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
# TODO: with this being a pseudo context manager'ish thing it doesn't actually handle shutdown
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
assert res is not None
|
||||
@ -352,7 +356,7 @@ async def test_get_roots(one_wallet_node_and_rpc: nodes_with_port, bt: BlockTool
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
assert res is not None
|
||||
@ -426,7 +430,7 @@ async def test_get_root_history(one_wallet_node_and_rpc: nodes_with_port, bt: Bl
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
assert res is not None
|
||||
@ -503,7 +507,7 @@ async def test_get_kv_diff(one_wallet_node_and_rpc: nodes_with_port, bt: BlockTo
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
assert res is not None
|
||||
@ -595,7 +599,7 @@ async def test_batch_update_matches_single_operations(
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
assert res is not None
|
||||
@ -705,7 +709,7 @@ async def test_get_owned_stores(one_wallet_node_and_rpc: nodes_with_port, bt: Bl
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
|
||||
expected_store_ids = []
|
||||
@ -741,7 +745,7 @@ async def test_subscriptions(one_wallet_node_and_rpc: nodes_with_port, bt: Block
|
||||
)
|
||||
await time_out_assert(15, wallet_node.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
|
||||
async for data_layer in init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path):
|
||||
async with init_data_layer(wallet_rpc_port=wallet_rpc_port, bt=bt, db_path=tmp_path) as data_layer:
|
||||
data_rpc_api = DataLayerRpcApi(data_layer)
|
||||
|
||||
res = await data_rpc_api.create_data_store({})
|
||||
|
@ -35,11 +35,11 @@ class TestRateLimits:
|
||||
# Too many messages
|
||||
r = RateLimiter(incoming=True)
|
||||
new_tx_message = make_msg(ProtocolMessageTypes.new_transaction, bytes([1] * 40))
|
||||
for i in range(4900):
|
||||
for i in range(4999):
|
||||
assert r.process_msg_and_check(new_tx_message, rl_v2, rl_v2)
|
||||
|
||||
saw_disconnect = False
|
||||
for i in range(4900):
|
||||
for i in range(4999):
|
||||
response = r.process_msg_and_check(new_tx_message, rl_v2, rl_v2)
|
||||
if not response:
|
||||
saw_disconnect = True
|
||||
@ -48,7 +48,7 @@ class TestRateLimits:
|
||||
# Non-tx message
|
||||
r = RateLimiter(incoming=True)
|
||||
new_peak_message = make_msg(ProtocolMessageTypes.new_peak, bytes([1] * 40))
|
||||
for i in range(20):
|
||||
for i in range(200):
|
||||
assert r.process_msg_and_check(new_peak_message, rl_v2, rl_v2)
|
||||
|
||||
saw_disconnect = False
|
||||
@ -80,7 +80,7 @@ class TestRateLimits:
|
||||
# Too much data
|
||||
r = RateLimiter(incoming=True)
|
||||
tx_message = make_msg(ProtocolMessageTypes.respond_transaction, bytes([1] * 500 * 1024))
|
||||
for i in range(10):
|
||||
for i in range(40):
|
||||
assert r.process_msg_and_check(tx_message, rl_v2, rl_v2)
|
||||
|
||||
saw_disconnect = False
|
||||
@ -110,14 +110,14 @@ class TestRateLimits:
|
||||
message_2 = make_msg(ProtocolMessageTypes.request_blocks, bytes([1] * 64))
|
||||
message_3 = make_msg(ProtocolMessageTypes.plot_sync_start, bytes([1] * 64))
|
||||
|
||||
for i in range(450):
|
||||
for i in range(500):
|
||||
assert r.process_msg_and_check(message_1, rl_v2, rl_v2)
|
||||
|
||||
for i in range(450):
|
||||
for i in range(500):
|
||||
assert r.process_msg_and_check(message_2, rl_v2, rl_v2)
|
||||
|
||||
saw_disconnect = False
|
||||
for i in range(450):
|
||||
for i in range(500):
|
||||
response = r.process_msg_and_check(message_3, rl_v2, rl_v2)
|
||||
if not response:
|
||||
saw_disconnect = True
|
||||
@ -158,11 +158,11 @@ class TestRateLimits:
|
||||
# Counts reset also
|
||||
r = RateLimiter(True, 5)
|
||||
new_tx_message = make_msg(ProtocolMessageTypes.new_transaction, bytes([1] * 40))
|
||||
for i in range(4900):
|
||||
for i in range(4999):
|
||||
assert r.process_msg_and_check(new_tx_message, rl_v2, rl_v2)
|
||||
|
||||
saw_disconnect = False
|
||||
for i in range(4900):
|
||||
for i in range(4999):
|
||||
response = r.process_msg_and_check(new_tx_message, rl_v2, rl_v2)
|
||||
if not response:
|
||||
saw_disconnect = True
|
||||
|
@ -19,7 +19,6 @@ from chia.util.ints import uint8, uint32, uint64
|
||||
from chia.util.streamable import (
|
||||
DefinitionError,
|
||||
Streamable,
|
||||
dataclass_from_dict,
|
||||
is_type_List,
|
||||
is_type_SpecificOptional,
|
||||
is_type_Tuple,
|
||||
@ -32,6 +31,7 @@ from chia.util.streamable import (
|
||||
parse_tuple,
|
||||
parse_uint32,
|
||||
streamable,
|
||||
streamable_from_dict,
|
||||
write_uint32,
|
||||
)
|
||||
from tests.block_tools import BlockTools
|
||||
@ -94,41 +94,27 @@ def test_plain_class_not_supported() -> None:
|
||||
a: PlainClass
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestDataclassFromDict1:
|
||||
a: int
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class StreamableFromDict1(Streamable):
|
||||
a: uint8
|
||||
b: str
|
||||
c: G1Element
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestDataclassFromDict2:
|
||||
a: TestDataclassFromDict1
|
||||
b: TestDataclassFromDict1
|
||||
c: float
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class StreamableFromDict2(Streamable):
|
||||
a: StreamableFromDict1
|
||||
b: StreamableFromDict1
|
||||
c: uint64
|
||||
|
||||
|
||||
def test_pure_dataclasses_in_dataclass_from_dict() -> None:
|
||||
|
||||
d1_dict = {"a": 1, "b": "2", "c": str(G1Element())}
|
||||
|
||||
d1: TestDataclassFromDict1 = dataclass_from_dict(TestDataclassFromDict1, d1_dict)
|
||||
assert d1.a == 1
|
||||
assert d1.b == "2"
|
||||
assert d1.c == G1Element()
|
||||
|
||||
d2_dict = {"a": d1, "b": d1_dict, "c": 1.2345}
|
||||
|
||||
d2: TestDataclassFromDict2 = dataclass_from_dict(TestDataclassFromDict2, d2_dict)
|
||||
assert d2.a == d1
|
||||
assert d2.b == d1
|
||||
assert d2.c == 1.2345
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConvertTupleFailures:
|
||||
a: Tuple[int, int]
|
||||
b: Tuple[int, Tuple[int, int]]
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class ConvertTupleFailures(Streamable):
|
||||
a: Tuple[uint8, uint8]
|
||||
b: Tuple[uint8, Tuple[uint8, uint8]]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -149,13 +135,14 @@ class ConvertTupleFailures:
|
||||
def test_convert_tuple_failures(input_dict: Dict[str, Any], error: Any) -> None:
|
||||
|
||||
with pytest.raises(error):
|
||||
dataclass_from_dict(ConvertTupleFailures, input_dict)
|
||||
streamable_from_dict(ConvertTupleFailures, input_dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConvertListFailures:
|
||||
a: List[int]
|
||||
b: List[List[int]]
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class ConvertListFailures(Streamable):
|
||||
a: List[uint8]
|
||||
b: List[List[uint8]]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@ -172,11 +159,12 @@ class ConvertListFailures:
|
||||
def test_convert_list_failures(input_dict: Dict[str, Any], error: Any) -> None:
|
||||
|
||||
with pytest.raises(error):
|
||||
dataclass_from_dict(ConvertListFailures, input_dict)
|
||||
streamable_from_dict(ConvertListFailures, input_dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConvertByteTypeFailures:
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class ConvertByteTypeFailures(Streamable):
|
||||
a: bytes4
|
||||
b: bytes
|
||||
|
||||
@ -201,11 +189,12 @@ class ConvertByteTypeFailures:
|
||||
def test_convert_byte_type_failures(input_dict: Dict[str, Any], error: Any) -> None:
|
||||
|
||||
with pytest.raises(error):
|
||||
dataclass_from_dict(ConvertByteTypeFailures, input_dict)
|
||||
streamable_from_dict(ConvertByteTypeFailures, input_dict)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConvertUnhashableTypeFailures:
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class ConvertUnhashableTypeFailures(Streamable):
|
||||
a: G1Element
|
||||
|
||||
|
||||
@ -226,7 +215,7 @@ class ConvertUnhashableTypeFailures:
|
||||
def test_convert_unhashable_type_failures(input_dict: Dict[str, Any], error: Any) -> None:
|
||||
|
||||
with pytest.raises(error):
|
||||
dataclass_from_dict(ConvertUnhashableTypeFailures, input_dict)
|
||||
streamable_from_dict(ConvertUnhashableTypeFailures, input_dict)
|
||||
|
||||
|
||||
class NoStrClass:
|
||||
@ -234,9 +223,10 @@ class NoStrClass:
|
||||
raise RuntimeError("No string")
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConvertPrimitiveFailures:
|
||||
a: int
|
||||
@streamable
|
||||
@dataclass(frozen=True)
|
||||
class ConvertPrimitiveFailures(Streamable):
|
||||
a: uint8
|
||||
b: uint8
|
||||
c: str
|
||||
|
||||
@ -252,28 +242,28 @@ class ConvertPrimitiveFailures:
|
||||
def test_convert_primitive_failures(input_dict: Dict[str, Any], error: Any) -> None:
|
||||
|
||||
with pytest.raises(error):
|
||||
dataclass_from_dict(ConvertPrimitiveFailures, input_dict)
|
||||
streamable_from_dict(ConvertPrimitiveFailures, input_dict)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_class, input_dict, error",
|
||||
[
|
||||
[TestDataclassFromDict1, {"a": "asdf", "b": "2", "c": G1Element()}, TypeError],
|
||||
[TestDataclassFromDict1, {"a": 1, "b": "2"}, KeyError],
|
||||
[TestDataclassFromDict1, {"a": 1, "b": "2", "c": "asd"}, TypeError],
|
||||
[TestDataclassFromDict1, {"a": 1, "b": "2", "c": "00" * G1Element.SIZE}, TypeError],
|
||||
[TestDataclassFromDict1, {"a": [], "b": "2", "c": G1Element()}, TypeError],
|
||||
[TestDataclassFromDict1, {"a": {}, "b": "2", "c": G1Element()}, TypeError],
|
||||
[TestDataclassFromDict2, {"a": "asdf", "b": 1.2345, "c": 1.2345}, TypeError],
|
||||
[TestDataclassFromDict2, {"a": 1.2345, "b": {"a": 1, "b": "2"}, "c": 1.2345}, TypeError],
|
||||
[TestDataclassFromDict2, {"a": {"a": 1, "b": "2", "c": G1Element()}, "b": {"a": 1, "b": "2"}}, KeyError],
|
||||
[TestDataclassFromDict2, {"a": {"a": 1, "b": "2"}, "b": {"a": 1, "b": "2"}, "c": 1.2345}, KeyError],
|
||||
[StreamableFromDict1, {"a": "asdf", "b": "2", "c": G1Element()}, TypeError],
|
||||
[StreamableFromDict1, {"a": 1, "b": "2"}, KeyError],
|
||||
[StreamableFromDict1, {"a": 1, "b": "2", "c": "asd"}, TypeError],
|
||||
[StreamableFromDict1, {"a": 1, "b": "2", "c": "00" * G1Element.SIZE}, TypeError],
|
||||
[StreamableFromDict1, {"a": [], "b": "2", "c": G1Element()}, TypeError],
|
||||
[StreamableFromDict1, {"a": {}, "b": "2", "c": G1Element()}, TypeError],
|
||||
[StreamableFromDict2, {"a": "asdf", "b": 12345, "c": 12345}, TypeError],
|
||||
[StreamableFromDict2, {"a": 12345, "b": {"a": 1, "b": "2"}, "c": 12345}, TypeError],
|
||||
[StreamableFromDict2, {"a": {"a": 1, "b": "2", "c": G1Element()}, "b": {"a": 1, "b": "2"}}, KeyError],
|
||||
[StreamableFromDict2, {"a": {"a": 1, "b": "2"}, "b": {"a": 1, "b": "2"}, "c": 12345}, KeyError],
|
||||
],
|
||||
)
|
||||
def test_dataclass_from_dict_failures(test_class: Type[Any], input_dict: Dict[str, Any], error: Any) -> None:
|
||||
def test_streamable_from_dict_failures(test_class: Type[Streamable], input_dict: Dict[str, Any], error: Any) -> None:
|
||||
|
||||
with pytest.raises(error):
|
||||
dataclass_from_dict(test_class, input_dict)
|
||||
streamable_from_dict(test_class, input_dict)
|
||||
|
||||
|
||||
@streamable
|
||||
|
@ -51,38 +51,35 @@ class TestWalletPoolStore:
|
||||
solution_0_alt: CoinSpend = make_child_solution(None, coin_0_alt)
|
||||
solution_1: CoinSpend = make_child_solution(solution_0)
|
||||
|
||||
assert store.get_spends_for_wallet(0) == []
|
||||
assert store.get_spends_for_wallet(1) == []
|
||||
assert await store.get_spends_for_wallet(0) == []
|
||||
assert await store.get_spends_for_wallet(1) == []
|
||||
|
||||
await store.add_spend(1, solution_1, 100, True)
|
||||
assert store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
assert await store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
|
||||
# Idempotent
|
||||
await store.add_spend(1, solution_1, 100, True)
|
||||
assert store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
assert await store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
await store.add_spend(1, solution_1, 101, True)
|
||||
|
||||
# Rebuild cache, no longer present
|
||||
await db_wrapper.rollback_transaction()
|
||||
await store.rebuild_cache()
|
||||
assert store.get_spends_for_wallet(1) == []
|
||||
assert await store.get_spends_for_wallet(1) == []
|
||||
|
||||
await store.rebuild_cache()
|
||||
await store.add_spend(1, solution_1, 100, False)
|
||||
assert store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
assert await store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
|
||||
solution_1_alt: CoinSpend = make_child_solution(solution_0_alt)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
await store.add_spend(1, solution_1_alt, 100, False)
|
||||
|
||||
assert store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
assert await store.get_spends_for_wallet(1) == [(100, solution_1)]
|
||||
|
||||
solution_2: CoinSpend = make_child_solution(solution_1)
|
||||
await store.add_spend(1, solution_2, 100, False)
|
||||
await store.rebuild_cache()
|
||||
solution_3: CoinSpend = make_child_solution(solution_2)
|
||||
await store.add_spend(1, solution_3, 100)
|
||||
solution_4: CoinSpend = make_child_solution(solution_3)
|
||||
@ -90,21 +87,16 @@ class TestWalletPoolStore:
|
||||
with pytest.raises(ValueError):
|
||||
await store.add_spend(1, solution_4, 99)
|
||||
|
||||
await store.rebuild_cache()
|
||||
await store.add_spend(1, solution_4, 101)
|
||||
await store.rebuild_cache()
|
||||
await store.rollback(101, 1, False)
|
||||
await store.rebuild_cache()
|
||||
assert store.get_spends_for_wallet(1) == [
|
||||
assert await store.get_spends_for_wallet(1) == [
|
||||
(100, solution_1),
|
||||
(100, solution_2),
|
||||
(100, solution_3),
|
||||
(101, solution_4),
|
||||
]
|
||||
await store.rebuild_cache()
|
||||
await store.rollback(100, 1, False)
|
||||
await store.rebuild_cache()
|
||||
assert store.get_spends_for_wallet(1) == [
|
||||
assert await store.get_spends_for_wallet(1) == [
|
||||
(100, solution_1),
|
||||
(100, solution_2),
|
||||
(100, solution_3),
|
||||
@ -116,7 +108,7 @@ class TestWalletPoolStore:
|
||||
solution_5: CoinSpend = make_child_solution(solution_4)
|
||||
await store.add_spend(1, solution_5, 105)
|
||||
await store.rollback(99, 1, False)
|
||||
assert store.get_spends_for_wallet(1) == []
|
||||
assert await store.get_spends_for_wallet(1) == []
|
||||
|
||||
finally:
|
||||
await db_connection.close()
|
||||
|
@ -118,12 +118,11 @@ async def setup_full_node(
|
||||
kwargs.update(
|
||||
parse_cli_args=False,
|
||||
connect_to_daemon=connect_to_daemon,
|
||||
service_name_prefix="test_",
|
||||
)
|
||||
if disable_capabilities is not None:
|
||||
kwargs.update(override_capabilities=get_capabilities(disable_capabilities))
|
||||
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
|
||||
await service.start()
|
||||
|
||||
@ -189,10 +188,9 @@ async def setup_wallet_node(
|
||||
kwargs.update(
|
||||
parse_cli_args=False,
|
||||
connect_to_daemon=False,
|
||||
service_name_prefix="test_",
|
||||
)
|
||||
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
|
||||
await service.start()
|
||||
|
||||
@ -229,10 +227,9 @@ async def setup_harvester(
|
||||
kwargs.update(
|
||||
parse_cli_args=False,
|
||||
connect_to_daemon=False,
|
||||
service_name_prefix="test_",
|
||||
)
|
||||
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
|
||||
if start_service:
|
||||
await service.start()
|
||||
@ -278,10 +275,9 @@ async def setup_farmer(
|
||||
kwargs.update(
|
||||
parse_cli_args=False,
|
||||
connect_to_daemon=False,
|
||||
service_name_prefix="test_",
|
||||
)
|
||||
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
|
||||
if start_service:
|
||||
await service.start()
|
||||
@ -301,10 +297,9 @@ async def setup_introducer(bt: BlockTools, port):
|
||||
advertised_port=port,
|
||||
parse_cli_args=False,
|
||||
connect_to_daemon=False,
|
||||
service_name_prefix="test_",
|
||||
)
|
||||
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
|
||||
await service.start()
|
||||
|
||||
@ -362,10 +357,9 @@ async def setup_timelord(
|
||||
kwargs.update(
|
||||
parse_cli_args=False,
|
||||
connect_to_daemon=False,
|
||||
service_name_prefix="test_",
|
||||
)
|
||||
|
||||
service = Service(**kwargs, running_new_process=False)
|
||||
service = Service(**kwargs)
|
||||
|
||||
await service.start()
|
||||
|
||||
|
@ -5,7 +5,7 @@ import pytest
|
||||
|
||||
from chia.consensus.block_rewards import calculate_base_farmer_reward, calculate_pool_reward
|
||||
from chia.full_node.mempool_manager import MempoolManager
|
||||
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
||||
from chia.simulator.simulator_protocol import FarmNewBlockProtocol, ReorgProtocol
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.peer_info import PeerInfo
|
||||
@ -98,6 +98,10 @@ class TestCATWallet:
|
||||
assert new_cat_wallet.cat_info.my_tail == cat_wallet.cat_info.my_tail
|
||||
assert await cat_wallet.lineage_store.get_all_lineage_proofs() == all_lineage
|
||||
|
||||
height = full_node_api.full_node.blockchain.get_peak_height()
|
||||
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(height - num_blocks - 1, height + 1, 32 * b"1"))
|
||||
await time_out_assert(15, cat_wallet.get_confirmed_balance, 0)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cat_creation_unique_lineage_store(self, self_hostname, two_wallet_nodes):
|
||||
num_blocks = 3
|
||||
@ -233,12 +237,15 @@ class TestCATWallet:
|
||||
15, tx_in_pool, True, full_node_api.full_node.mempool_manager, tx_record.spend_bundle.name()
|
||||
)
|
||||
|
||||
for i in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
|
||||
await time_out_assert(15, cat_wallet.get_confirmed_balance, 55)
|
||||
await time_out_assert(15, cat_wallet.get_unconfirmed_balance, 55)
|
||||
|
||||
height = full_node_api.full_node.blockchain.get_peak_height()
|
||||
await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(height - 1, height + 1, 32 * b"1"))
|
||||
await time_out_assert(15, cat_wallet.get_confirmed_balance, 40)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trusted",
|
||||
[True, False],
|
||||
|
@ -13,6 +13,7 @@ from chia.simulator.simulator_protocol import FarmNewBlockProtocol
|
||||
from chia.types.blockchain_format.program import Program
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.peer_info import PeerInfo
|
||||
from chia.types.spend_bundle import SpendBundle
|
||||
from chia.util.bech32m import encode_puzzle_hash
|
||||
from chia.util.byte_types import hexstr_to_bytes
|
||||
from chia.util.ints import uint16, uint32, uint64
|
||||
@ -50,9 +51,10 @@ async def wait_rpc_state_condition(
|
||||
return {}
|
||||
|
||||
|
||||
async def make_new_block_with(resp, full_node_api, ph):
|
||||
async def make_new_block_with(resp: Dict, full_node_api: FullNodeSimulator, ph: bytes32) -> SpendBundle:
|
||||
assert resp.get("success")
|
||||
sb = resp["spend_bundle"]
|
||||
assert isinstance(sb, SpendBundle)
|
||||
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
return sb
|
||||
@ -863,8 +865,7 @@ async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) ->
|
||||
spend_bundle = spend_bundle_list[0].spend_bundle
|
||||
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, spend_bundle.name())
|
||||
|
||||
for _ in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
await time_out_assert(15, wallet_0.get_pending_change_balance, 0)
|
||||
hex_did_id = did_wallet.get_my_DID()
|
||||
hmr_did_id = encode_puzzle_hash(bytes32.from_hexstr(hex_did_id), DID_HRP)
|
||||
@ -886,22 +887,14 @@ async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) ->
|
||||
"fee": fee,
|
||||
}
|
||||
)
|
||||
assert resp.get("success")
|
||||
sb = resp["spend_bundle"]
|
||||
|
||||
# ensure hints are generated
|
||||
assert compute_memos(sb)
|
||||
await time_out_assert_not_none(5, full_node_api.full_node.mempool_manager.get_spendbundle, sb.name())
|
||||
|
||||
for i in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph1))
|
||||
await make_new_block_with(resp, full_node_api, ph1)
|
||||
|
||||
# Check DID NFT
|
||||
coins_response = await wait_rpc_state_condition(
|
||||
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: x["nft_list"]
|
||||
)
|
||||
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 7999999999898)
|
||||
await time_out_assert(10, wallet_0.get_confirmed_balance, 7999999999898)
|
||||
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 5999999999898)
|
||||
await time_out_assert(10, wallet_0.get_confirmed_balance, 5999999999898)
|
||||
coins = coins_response["nft_list"]
|
||||
assert len(coins) == 1
|
||||
assert coins[0].owner_did.hex() == hex_did_id
|
||||
@ -929,8 +922,8 @@ async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) ->
|
||||
await wait_rpc_state_condition(
|
||||
5, api_0.nft_get_nfts, [dict(wallet_id=nft_wallet_0_id)], lambda x: not x["nft_list"]
|
||||
)
|
||||
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 7999999999798)
|
||||
await time_out_assert(10, wallet_0.get_confirmed_balance, 7999999999798)
|
||||
await time_out_assert(10, wallet_0.get_unconfirmed_balance, 5999999999798)
|
||||
await time_out_assert(10, wallet_0.get_confirmed_balance, 5999999999798)
|
||||
# wait for all wallets to be created
|
||||
await time_out_assert(10, len, 3, wallet_1.wallet_state_manager.wallets)
|
||||
did_wallet_1 = wallet_1.wallet_state_manager.wallets[3]
|
||||
@ -946,22 +939,19 @@ async def test_nft_transfer_nft_with_did(two_wallet_nodes: Any, trusted: Any) ->
|
||||
assert coins_response["nft_list"][0].owner_did is None
|
||||
nft_coin_id = coins_response["nft_list"][0].nft_coin_id
|
||||
|
||||
await time_out_assert(10, did_wallet_1.get_spendable_balance, 1)
|
||||
await time_out_assert(20, did_wallet_1.get_spendable_balance, 1)
|
||||
|
||||
# Set DID
|
||||
resp = await api_1.nft_set_nft_did(
|
||||
dict(wallet_id=nft_wallet_id_1, did_id=hmr_did_id, nft_coin_id=nft_coin_id.hex(), fee=fee)
|
||||
)
|
||||
assert resp.get("success")
|
||||
|
||||
for i in range(1, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
await make_new_block_with(resp, full_node_api, ph)
|
||||
|
||||
coins_response = await wait_rpc_state_condition(
|
||||
5, api_1.nft_get_by_did, [dict(did_id=hmr_did_id)], lambda x: x.get("wallet_id", 0) > 0
|
||||
)
|
||||
await time_out_assert(10, wallet_1.get_unconfirmed_balance, 12000000000100)
|
||||
await time_out_assert(10, wallet_1.get_confirmed_balance, 12000000000100)
|
||||
await time_out_assert(10, wallet_1.get_unconfirmed_balance, 10000000000100)
|
||||
await time_out_assert(10, wallet_1.get_confirmed_balance, 10000000000100)
|
||||
nft_wallet_1_id = coins_response.get("wallet_id")
|
||||
assert nft_wallet_1_id
|
||||
# Check NFT DID is set now
|
||||
|
@ -856,7 +856,14 @@ async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
||||
for _ in range(3):
|
||||
await farm_transaction_block(full_node_api, wallet_1_node)
|
||||
|
||||
await time_out_assert(15, wallet_is_synced, True, wallet_1_node, full_node_api)
|
||||
nft_wallet: NFTWallet = wallet_1_node.wallet_state_manager.wallets[nft_wallet_id]
|
||||
|
||||
def have_nfts():
|
||||
return len(nft_wallet.get_current_nfts()) > 0
|
||||
|
||||
await time_out_assert(15, have_nfts, True)
|
||||
|
||||
# Test with the hex version of nft_id
|
||||
nft_id = nft_wallet.get_current_nfts()[0].coin.name().hex()
|
||||
nft_info = (await wallet_1_rpc.get_nft_info(nft_id))["nft_info"]
|
||||
@ -872,6 +879,7 @@ async def test_nft_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment):
|
||||
|
||||
for _ in range(3):
|
||||
await farm_transaction_block(full_node_api, wallet_1_node)
|
||||
await time_out_assert(15, wallet_is_synced, True, wallet_1_node, full_node_api)
|
||||
|
||||
nft_wallet_id_1 = (
|
||||
await wallet_2_node.wallet_state_manager.get_all_wallet_info_entries(wallet_type=WalletType.NFT)
|
||||
|
@ -160,9 +160,8 @@ class TestWalletSync:
|
||||
|
||||
# Tests a reorg with the wallet
|
||||
num_blocks = 30
|
||||
blocks_reorg = bt.get_consecutive_blocks(
|
||||
num_blocks, block_list_input=default_400_blocks[:-5], current_time=True
|
||||
)
|
||||
blocks_reorg = bt.get_consecutive_blocks(num_blocks - 1, block_list_input=default_400_blocks[:-5])
|
||||
blocks_reorg = bt.get_consecutive_blocks(1, blocks_reorg, guarantee_transaction_block=True, current_time=True)
|
||||
for i in range(1, len(blocks_reorg)):
|
||||
await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks_reorg[i]))
|
||||
|
||||
|
652
tests/wallet/test_transaction_store.py
Normal file
652
tests/wallet/test_transaction_store.py
Normal file
@ -0,0 +1,652 @@
|
||||
import dataclasses
|
||||
from secrets import token_bytes
|
||||
from typing import Any, List
|
||||
|
||||
import pytest
|
||||
|
||||
from chia.types.blockchain_format.coin import Coin
|
||||
from chia.types.blockchain_format.sized_bytes import bytes32
|
||||
from chia.types.mempool_inclusion_status import MempoolInclusionStatus
|
||||
from chia.util.errors import Err
|
||||
from chia.util.ints import uint8, uint32, uint64
|
||||
from chia.wallet.transaction_record import TransactionRecord
|
||||
from chia.wallet.util.transaction_type import TransactionType
|
||||
from chia.wallet.wallet_transaction_store import WalletTransactionStore, filter_ok_mempool_status
|
||||
from tests.util.db_connection import DBConnection1
|
||||
|
||||
coin_1 = Coin(token_bytes(32), token_bytes(32), uint64(12312))
|
||||
coin_2 = Coin(token_bytes(32), token_bytes(32), uint64(1234))
|
||||
coin_3 = Coin(token_bytes(32), token_bytes(32), uint64(12312 - 1234))
|
||||
|
||||
tr1 = TransactionRecord(
|
||||
uint32(0), # confirmed height
|
||||
uint64(1000), # created_at_time
|
||||
bytes32(token_bytes(32)), # to_puzzle_hash
|
||||
uint64(1234), # amount
|
||||
uint64(12), # fee_amount
|
||||
False, # confirmed
|
||||
uint32(0), # sent
|
||||
None, # Optional[SpendBundle] spend_bundle
|
||||
[coin_2, coin_3], # additions
|
||||
[coin_1], # removals
|
||||
uint32(1), # wallet_id
|
||||
[], # List[Tuple[str, uint8, Optional[str]]] sent_to
|
||||
bytes32(token_bytes(32)), # trade_id
|
||||
uint32(TransactionType.OUTGOING_TX), # type
|
||||
bytes32(token_bytes(32)), # name
|
||||
[], # List[Tuple[bytes32, List[bytes]]] memos
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
assert await store.get_transaction_record(tr1.name) is None
|
||||
await store.add_transaction_record(tr1, False)
|
||||
assert await store.get_transaction_record(tr1.name) == tr1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
assert await store.get_transaction_record(tr1.name) == tr1
|
||||
await store.delete_transaction_record(tr1.name)
|
||||
assert await store.get_transaction_record(tr1.name) is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_confirmed() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.set_confirmed(tr1.name, uint32(100))
|
||||
|
||||
assert await store.get_transaction_record(tr1.name) == dataclasses.replace(
|
||||
tr1, confirmed=True, confirmed_at_height=uint32(100)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_increment_sent_noop() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
assert (
|
||||
await store.increment_sent(bytes32(token_bytes(32)), "peer1", MempoolInclusionStatus.PENDING, None) is False
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_increment_sent() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 0
|
||||
assert tr.sent_to == []
|
||||
|
||||
assert await store.increment_sent(tr1.name, "peer1", MempoolInclusionStatus.PENDING, None) is True
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 1
|
||||
assert tr.sent_to == [("peer1", uint8(2), None)]
|
||||
|
||||
assert await store.increment_sent(tr1.name, "peer1", MempoolInclusionStatus.SUCCESS, None) is True
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 1
|
||||
assert tr.sent_to == [("peer1", uint8(2), None), ("peer1", uint8(1), None)]
|
||||
|
||||
assert await store.increment_sent(tr1.name, "peer2", MempoolInclusionStatus.SUCCESS, None) is True
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 2
|
||||
assert tr.sent_to == [("peer1", uint8(2), None), ("peer1", uint8(1), None), ("peer2", uint8(1), None)]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_increment_sent_error() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 0
|
||||
assert tr.sent_to == []
|
||||
|
||||
await store.increment_sent(tr1.name, "peer1", MempoolInclusionStatus.FAILED, Err.MEMPOOL_NOT_INITIALIZED)
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 1
|
||||
assert tr.sent_to == [("peer1", uint8(3), "MEMPOOL_NOT_INITIALIZED")]
|
||||
|
||||
|
||||
def test_filter_ok_mempool_status() -> None:
|
||||
assert filter_ok_mempool_status([("peer1", uint8(1), None)]) == []
|
||||
assert filter_ok_mempool_status([("peer1", uint8(2), None)]) == []
|
||||
assert filter_ok_mempool_status([("peer1", uint8(3), None)]) == [("peer1", uint8(3), None)]
|
||||
assert filter_ok_mempool_status(
|
||||
[("peer1", uint8(2), None), ("peer1", uint8(1), None), ("peer1", uint8(3), None)]
|
||||
) == [("peer1", uint8(3), None)]
|
||||
|
||||
assert filter_ok_mempool_status([("peer1", uint8(3), "message does not matter")]) == [
|
||||
("peer1", uint8(3), "message does not matter")
|
||||
]
|
||||
assert filter_ok_mempool_status([("peer1", uint8(2), "message does not matter")]) == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tx_reorged_update() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr = dataclasses.replace(tr1, sent=2, sent_to=[("peer1", uint8(1), None), ("peer2", uint8(1), None)])
|
||||
await store.add_transaction_record(tr, False)
|
||||
tr = await store.get_transaction_record(tr.name)
|
||||
assert tr.sent == 2
|
||||
assert tr.sent_to == [("peer1", uint8(1), None), ("peer2", uint8(1), None)]
|
||||
|
||||
await store.tx_reorged(tr, False)
|
||||
tr = await store.get_transaction_record(tr1.name)
|
||||
assert tr.sent == 0
|
||||
assert tr.sent_to == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_tx_reorged_add() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr = dataclasses.replace(tr1, sent=2, sent_to=[("peer1", uint8(1), None), ("peer2", uint8(1), None)])
|
||||
|
||||
await store.get_transaction_record(tr.name) is None
|
||||
await store.tx_reorged(tr, False)
|
||||
tr = await store.get_transaction_record(tr.name)
|
||||
assert tr.sent == 0
|
||||
assert tr.sent_to == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_tx_record() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32))
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32))
|
||||
|
||||
assert await store.get_transaction_record(tr1.name) is None
|
||||
await store.add_transaction_record(tr1, False)
|
||||
assert await store.get_transaction_record(tr1.name) == tr1
|
||||
|
||||
assert await store.get_transaction_record(tr2.name) is None
|
||||
await store.add_transaction_record(tr2, False)
|
||||
assert await store.get_transaction_record(tr2.name) == tr2
|
||||
|
||||
assert await store.get_transaction_record(tr3.name) is None
|
||||
await store.add_transaction_record(tr3, False)
|
||||
assert await store.get_transaction_record(tr3.name) == tr3
|
||||
|
||||
assert await store.get_transaction_record(tr1.name) == tr1
|
||||
assert await store.get_transaction_record(tr2.name) == tr2
|
||||
assert await store.get_transaction_record(tr3.name) == tr3
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_farming_rewards() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
test_trs: List[TransactionRecord] = []
|
||||
# tr1 is type OUTGOING_TX
|
||||
|
||||
for conf in [True, False]:
|
||||
for type in [
|
||||
TransactionType.INCOMING_TX,
|
||||
TransactionType.OUTGOING_TX,
|
||||
TransactionType.COINBASE_REWARD,
|
||||
TransactionType.FEE_REWARD,
|
||||
TransactionType.INCOMING_TRADE,
|
||||
TransactionType.OUTGOING_TRADE,
|
||||
]:
|
||||
test_trs.append(
|
||||
dataclasses.replace(
|
||||
tr1,
|
||||
name=token_bytes(32),
|
||||
confirmed=conf,
|
||||
confirmed_at_height=uint32(100 if conf else 0),
|
||||
type=type,
|
||||
)
|
||||
)
|
||||
|
||||
for tr in test_trs:
|
||||
await store.add_transaction_record(tr, False)
|
||||
assert await store.get_transaction_record(tr.name) == tr
|
||||
|
||||
rewards = await store.get_farming_rewards()
|
||||
assert len(rewards) == 2
|
||||
assert test_trs[2] in rewards
|
||||
assert test_trs[3] in rewards
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_all_unconfirmed() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(100))
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(tr2, False)
|
||||
|
||||
assert await store.get_all_unconfirmed() == [tr1]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_unconfirmed_for_wallet() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(100))
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32), wallet_id=2)
|
||||
tr4 = dataclasses.replace(tr2, name=token_bytes(32), wallet_id=2)
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(tr2, False)
|
||||
await store.add_transaction_record(tr3, False)
|
||||
await store.add_transaction_record(tr4, False)
|
||||
|
||||
assert await store.get_unconfirmed_for_wallet(1) == [tr1]
|
||||
assert await store.get_unconfirmed_for_wallet(2) == [tr3]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_transaction_count_for_wallet() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), wallet_id=2)
|
||||
|
||||
# 5 transactions in wallet_id 1
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(dataclasses.replace(tr1, name=token_bytes(32)), False)
|
||||
await store.add_transaction_record(dataclasses.replace(tr1, name=token_bytes(32)), False)
|
||||
await store.add_transaction_record(dataclasses.replace(tr1, name=token_bytes(32)), False)
|
||||
await store.add_transaction_record(dataclasses.replace(tr1, name=token_bytes(32)), False)
|
||||
|
||||
# 2 transactions in wallet_id 2
|
||||
await store.add_transaction_record(tr2, False)
|
||||
await store.add_transaction_record(dataclasses.replace(tr2, name=token_bytes(32)), False)
|
||||
|
||||
assert await store.get_transaction_count_for_wallet(1) == 5
|
||||
assert await store.get_transaction_count_for_wallet(2) == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_all_transactions_for_wallet() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
test_trs: List[TransactionRecord] = []
|
||||
for wallet_id in [1, 2]:
|
||||
for type in [
|
||||
TransactionType.INCOMING_TX,
|
||||
TransactionType.OUTGOING_TX,
|
||||
TransactionType.COINBASE_REWARD,
|
||||
TransactionType.FEE_REWARD,
|
||||
TransactionType.INCOMING_TRADE,
|
||||
TransactionType.OUTGOING_TRADE,
|
||||
]:
|
||||
test_trs.append(dataclasses.replace(tr1, name=token_bytes(32), wallet_id=wallet_id, type=type))
|
||||
|
||||
for tr in test_trs:
|
||||
await store.add_transaction_record(tr, False)
|
||||
|
||||
assert await store.get_all_transactions_for_wallet(1) == test_trs[:6]
|
||||
assert await store.get_all_transactions_for_wallet(2) == test_trs[6:]
|
||||
|
||||
assert await store.get_all_transactions_for_wallet(1, TransactionType.INCOMING_TX) == [test_trs[0]]
|
||||
assert await store.get_all_transactions_for_wallet(1, TransactionType.OUTGOING_TX) == [test_trs[1]]
|
||||
assert await store.get_all_transactions_for_wallet(1, TransactionType.INCOMING_TRADE) == [test_trs[4]]
|
||||
assert await store.get_all_transactions_for_wallet(1, TransactionType.OUTGOING_TRADE) == [test_trs[5]]
|
||||
|
||||
assert await store.get_all_transactions_for_wallet(2, TransactionType.INCOMING_TX) == [test_trs[6]]
|
||||
assert await store.get_all_transactions_for_wallet(2, TransactionType.OUTGOING_TX) == [test_trs[7]]
|
||||
assert await store.get_all_transactions_for_wallet(2, TransactionType.INCOMING_TRADE) == [test_trs[10]]
|
||||
assert await store.get_all_transactions_for_wallet(2, TransactionType.OUTGOING_TRADE) == [test_trs[11]]
|
||||
|
||||
|
||||
def cmp(lhs: List[Any], rhs: List[Any]) -> bool:
|
||||
if len(rhs) != len(lhs):
|
||||
return False
|
||||
|
||||
for e in lhs:
|
||||
if e not in rhs:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_all_transactions() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
test_trs: List[TransactionRecord] = []
|
||||
assert await store.get_all_transactions() == []
|
||||
for wallet_id in [1, 2, 3, 4]:
|
||||
test_trs.append(dataclasses.replace(tr1, name=token_bytes(32), wallet_id=wallet_id))
|
||||
|
||||
for tr in test_trs:
|
||||
await store.add_transaction_record(tr, False)
|
||||
|
||||
all_trs = await store.get_all_transactions()
|
||||
assert cmp(all_trs, test_trs)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_transaction_above() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
test_trs: List[TransactionRecord] = []
|
||||
assert await store.get_transaction_above(uint32(0)) == []
|
||||
for height in range(10):
|
||||
test_trs.append(dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(height)))
|
||||
|
||||
for tr in test_trs:
|
||||
await store.add_transaction_record(tr, False)
|
||||
|
||||
for height in range(10):
|
||||
trs = await store.get_transaction_above(uint32(height))
|
||||
assert cmp(trs, test_trs[height + 1 :])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_tx_by_trade_id() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), trade_id=token_bytes(32))
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32), trade_id=token_bytes(32))
|
||||
tr4 = dataclasses.replace(tr1, name=token_bytes(32))
|
||||
|
||||
assert await store.get_transactions_by_trade_id(tr1.trade_id) == []
|
||||
await store.add_transaction_record(tr1, False)
|
||||
assert await store.get_transactions_by_trade_id(tr1.trade_id) == [tr1]
|
||||
|
||||
assert await store.get_transactions_by_trade_id(tr2.trade_id) == []
|
||||
await store.add_transaction_record(tr2, False)
|
||||
assert await store.get_transactions_by_trade_id(tr2.trade_id) == [tr2]
|
||||
|
||||
assert await store.get_transactions_by_trade_id(tr3.trade_id) == []
|
||||
await store.add_transaction_record(tr3, False)
|
||||
assert await store.get_transactions_by_trade_id(tr3.trade_id) == [tr3]
|
||||
|
||||
# tr1 and tr4 have the same trade_id
|
||||
assert await store.get_transactions_by_trade_id(tr4.trade_id) == [tr1]
|
||||
await store.add_transaction_record(tr4, False)
|
||||
assert cmp(await store.get_transactions_by_trade_id(tr4.trade_id), [tr1, tr4])
|
||||
|
||||
assert cmp(await store.get_transactions_by_trade_id(tr1.trade_id), [tr1, tr4])
|
||||
assert await store.get_transactions_by_trade_id(tr2.trade_id) == [tr2]
|
||||
assert await store.get_transactions_by_trade_id(tr3.trade_id) == [tr3]
|
||||
assert cmp(await store.get_transactions_by_trade_id(tr4.trade_id), [tr1, tr4])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rollback_to_block() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
test_trs: List[TransactionRecord] = []
|
||||
for height in range(10):
|
||||
test_trs.append(dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(height)))
|
||||
|
||||
for tr in test_trs:
|
||||
await store.add_transaction_record(tr, False)
|
||||
|
||||
await store.rollback_to_block(uint32(6))
|
||||
all_trs = await store.get_all_transactions()
|
||||
assert cmp(all_trs, test_trs[:7])
|
||||
|
||||
await store.rollback_to_block(uint32(5))
|
||||
all_trs = await store.get_all_transactions()
|
||||
assert cmp(all_trs, test_trs[:6])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_unconfirmed() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), confirmed=True)
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32), confirmed=True, wallet_id=2)
|
||||
tr4 = dataclasses.replace(tr1, name=token_bytes(32), wallet_id=2)
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(tr2, False)
|
||||
await store.add_transaction_record(tr3, False)
|
||||
await store.add_transaction_record(tr4, False)
|
||||
|
||||
assert cmp(await store.get_all_transactions(), [tr1, tr2, tr3, tr4])
|
||||
await store.delete_unconfirmed_transactions(1)
|
||||
assert cmp(await store.get_all_transactions(), [tr2, tr3, tr4])
|
||||
await store.delete_unconfirmed_transactions(2)
|
||||
assert cmp(await store.get_all_transactions(), [tr2, tr3])
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_transactions_between_confirmed() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(1))
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(2))
|
||||
tr4 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(3))
|
||||
tr5 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(4))
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(tr2, False)
|
||||
await store.add_transaction_record(tr3, False)
|
||||
await store.add_transaction_record(tr4, False)
|
||||
await store.add_transaction_record(tr5, False)
|
||||
|
||||
# test different limits
|
||||
assert await store.get_transactions_between(1, 0, 1) == [tr1]
|
||||
assert await store.get_transactions_between(1, 0, 2) == [tr1, tr2]
|
||||
assert await store.get_transactions_between(1, 0, 3) == [tr1, tr2, tr3]
|
||||
assert await store.get_transactions_between(1, 0, 100) == [tr1, tr2, tr3, tr4, tr5]
|
||||
|
||||
# test different start offsets
|
||||
assert await store.get_transactions_between(1, 1, 100) == [tr2, tr3, tr4, tr5]
|
||||
assert await store.get_transactions_between(1, 2, 100) == [tr3, tr4, tr5]
|
||||
assert await store.get_transactions_between(1, 3, 100) == [tr4, tr5]
|
||||
|
||||
# wallet 2 is empty
|
||||
assert await store.get_transactions_between(2, 0, 100) == []
|
||||
|
||||
# reverse
|
||||
|
||||
# test different limits
|
||||
assert await store.get_transactions_between(1, 0, 1, reverse=True) == [tr5]
|
||||
assert await store.get_transactions_between(1, 0, 2, reverse=True) == [tr5, tr4]
|
||||
assert await store.get_transactions_between(1, 0, 3, reverse=True) == [tr5, tr4, tr3]
|
||||
assert await store.get_transactions_between(1, 0, 100, reverse=True) == [tr5, tr4, tr3, tr2, tr1]
|
||||
|
||||
# test different start offsets
|
||||
assert await store.get_transactions_between(1, 1, 100, reverse=True) == [tr4, tr3, tr2, tr1]
|
||||
assert await store.get_transactions_between(1, 2, 100, reverse=True) == [tr3, tr2, tr1]
|
||||
assert await store.get_transactions_between(1, 3, 100, reverse=True) == [tr2, tr1]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_transactions_between_relevance() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
t1 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=False, confirmed_at_height=uint32(2), created_at_time=1000
|
||||
)
|
||||
t2 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=False, confirmed_at_height=uint32(2), created_at_time=999
|
||||
)
|
||||
t3 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=False, confirmed_at_height=uint32(1), created_at_time=1000
|
||||
)
|
||||
t4 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=False, confirmed_at_height=uint32(1), created_at_time=999
|
||||
)
|
||||
|
||||
t5 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(2), created_at_time=1000
|
||||
)
|
||||
t6 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(2), created_at_time=999
|
||||
)
|
||||
t7 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(1), created_at_time=1000
|
||||
)
|
||||
t8 = dataclasses.replace(
|
||||
tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(1), created_at_time=999
|
||||
)
|
||||
|
||||
await store.add_transaction_record(t1, False)
|
||||
await store.add_transaction_record(t2, False)
|
||||
await store.add_transaction_record(t3, False)
|
||||
await store.add_transaction_record(t4, False)
|
||||
await store.add_transaction_record(t5, False)
|
||||
await store.add_transaction_record(t6, False)
|
||||
await store.add_transaction_record(t7, False)
|
||||
await store.add_transaction_record(t8, False)
|
||||
|
||||
# test different limits
|
||||
assert await store.get_transactions_between(1, 0, 1, sort_key="RELEVANCE") == [t1]
|
||||
assert await store.get_transactions_between(1, 0, 2, sort_key="RELEVANCE") == [t1, t2]
|
||||
assert await store.get_transactions_between(1, 0, 3, sort_key="RELEVANCE") == [t1, t2, t3]
|
||||
assert await store.get_transactions_between(1, 0, 100, sort_key="RELEVANCE") == [t1, t2, t3, t4, t5, t6, t7, t8]
|
||||
|
||||
# test different start offsets
|
||||
assert await store.get_transactions_between(1, 1, 100, sort_key="RELEVANCE") == [t2, t3, t4, t5, t6, t7, t8]
|
||||
assert await store.get_transactions_between(1, 2, 100, sort_key="RELEVANCE") == [t3, t4, t5, t6, t7, t8]
|
||||
assert await store.get_transactions_between(1, 3, 100, sort_key="RELEVANCE") == [t4, t5, t6, t7, t8]
|
||||
assert await store.get_transactions_between(1, 4, 100, sort_key="RELEVANCE") == [t5, t6, t7, t8]
|
||||
|
||||
# wallet 2 is empty
|
||||
assert await store.get_transactions_between(2, 0, 100, sort_key="RELEVANCE") == []
|
||||
|
||||
# reverse
|
||||
|
||||
# test different limits
|
||||
assert await store.get_transactions_between(1, 0, 1, sort_key="RELEVANCE", reverse=True) == [t8]
|
||||
assert await store.get_transactions_between(1, 0, 2, sort_key="RELEVANCE", reverse=True) == [t8, t7]
|
||||
assert await store.get_transactions_between(1, 0, 3, sort_key="RELEVANCE", reverse=True) == [t8, t7, t6]
|
||||
assert await store.get_transactions_between(1, 0, 100, sort_key="RELEVANCE", reverse=True) == [
|
||||
t8,
|
||||
t7,
|
||||
t6,
|
||||
t5,
|
||||
t4,
|
||||
t3,
|
||||
t2,
|
||||
t1,
|
||||
]
|
||||
|
||||
# test different start offsets
|
||||
assert await store.get_transactions_between(1, 1, 100, sort_key="RELEVANCE", reverse=True) == [
|
||||
t7,
|
||||
t6,
|
||||
t5,
|
||||
t4,
|
||||
t3,
|
||||
t2,
|
||||
t1,
|
||||
]
|
||||
assert await store.get_transactions_between(1, 2, 100, sort_key="RELEVANCE", reverse=True) == [
|
||||
t6,
|
||||
t5,
|
||||
t4,
|
||||
t3,
|
||||
t2,
|
||||
t1,
|
||||
]
|
||||
assert await store.get_transactions_between(1, 3, 100, sort_key="RELEVANCE", reverse=True) == [
|
||||
t5,
|
||||
t4,
|
||||
t3,
|
||||
t2,
|
||||
t1,
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_transactions_between_to_puzzle_hash() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
ph1 = token_bytes(32)
|
||||
ph2 = token_bytes(32)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(1), to_puzzle_hash=ph1)
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(2), to_puzzle_hash=ph1)
|
||||
tr4 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(3), to_puzzle_hash=ph2)
|
||||
tr5 = dataclasses.replace(tr1, name=token_bytes(32), confirmed_at_height=uint32(4), to_puzzle_hash=ph2)
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(tr2, False)
|
||||
await store.add_transaction_record(tr3, False)
|
||||
await store.add_transaction_record(tr4, False)
|
||||
await store.add_transaction_record(tr5, False)
|
||||
|
||||
# test different limits
|
||||
assert await store.get_transactions_between(1, 0, 100, to_puzzle_hash=ph1) == [tr2, tr3]
|
||||
assert await store.get_transactions_between(1, 0, 100, to_puzzle_hash=ph2) == [tr4, tr5]
|
||||
|
||||
# test different start offsets
|
||||
assert await store.get_transactions_between(1, 1, 100, to_puzzle_hash=ph1) == [tr3]
|
||||
assert await store.get_transactions_between(1, 1, 100, to_puzzle_hash=ph2) == [tr5]
|
||||
|
||||
# reverse
|
||||
|
||||
# test different limits
|
||||
assert await store.get_transactions_between(1, 0, 100, to_puzzle_hash=ph1, reverse=True) == [tr3, tr2]
|
||||
assert await store.get_transactions_between(1, 0, 100, to_puzzle_hash=ph2, reverse=True) == [tr5, tr4]
|
||||
|
||||
# test different start offsets
|
||||
assert await store.get_transactions_between(1, 1, 100, to_puzzle_hash=ph1, reverse=True) == [tr2]
|
||||
assert await store.get_transactions_between(1, 1, 100, to_puzzle_hash=ph2, reverse=True) == [tr4]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_not_sent() -> None:
|
||||
async with DBConnection1() as db_wrapper:
|
||||
store = await WalletTransactionStore.create(db_wrapper)
|
||||
|
||||
tr2 = dataclasses.replace(tr1, name=token_bytes(32), confirmed=True, confirmed_at_height=uint32(1))
|
||||
tr3 = dataclasses.replace(tr1, name=token_bytes(32))
|
||||
tr4 = dataclasses.replace(tr1, name=token_bytes(32))
|
||||
|
||||
await store.add_transaction_record(tr1, False)
|
||||
await store.add_transaction_record(tr2, False)
|
||||
await store.add_transaction_record(tr3, False)
|
||||
await store.add_transaction_record(tr4, False)
|
||||
|
||||
not_sent = await store.get_not_sent()
|
||||
assert cmp(not_sent, [tr1, tr3, tr4])
|
||||
|
||||
not_sent = await store.get_not_sent()
|
||||
assert cmp(not_sent, [tr1, tr3, tr4])
|
||||
|
||||
not_sent = await store.get_not_sent()
|
||||
assert cmp(not_sent, [tr1, tr3, tr4])
|
||||
|
||||
not_sent = await store.get_not_sent()
|
||||
assert cmp(not_sent, [tr1, tr3, tr4])
|
||||
|
||||
not_sent = await store.get_not_sent()
|
||||
assert cmp(not_sent, [tr1, tr3, tr4])
|
||||
|
||||
# the 6th time we call this function, we don't get any unsent txs
|
||||
not_sent = await store.get_not_sent()
|
||||
assert cmp(not_sent, [])
|
||||
|
||||
# TODO: also cover include_accepted_txs=True
|
@ -85,7 +85,7 @@ class TestWalletSimulator:
|
||||
return True
|
||||
|
||||
await time_out_assert(20, check_tx_are_pool_farm_rewards, True)
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trusted",
|
||||
@ -123,7 +123,7 @@ class TestWalletSimulator:
|
||||
)
|
||||
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds)
|
||||
|
||||
tx = await wallet.generate_signed_transaction(
|
||||
uint64(10),
|
||||
@ -132,9 +132,9 @@ class TestWalletSimulator:
|
||||
)
|
||||
await wallet.push_transaction(tx)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds - 10)
|
||||
await time_out_assert(5, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds - 10)
|
||||
await time_out_assert(10, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
|
||||
for i in range(0, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph))
|
||||
@ -146,8 +146,8 @@ class TestWalletSimulator:
|
||||
]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, new_funds - 10)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, new_funds - 10)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, new_funds - 10)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, new_funds - 10)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trusted",
|
||||
@ -193,7 +193,7 @@ class TestWalletSimulator:
|
||||
]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trusted",
|
||||
@ -250,7 +250,7 @@ class TestWalletSimulator:
|
||||
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet_0.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet_0.wallet_state_manager.main_wallet.get_confirmed_balance, funds)
|
||||
|
||||
tx = await wallet_0.wallet_state_manager.main_wallet.generate_signed_transaction(
|
||||
uint64(10), bytes32(32 * b"0"), uint64(0)
|
||||
@ -435,8 +435,8 @@ class TestWalletSimulator:
|
||||
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds)
|
||||
|
||||
assert await wallet.get_confirmed_balance() == funds
|
||||
assert await wallet.get_unconfirmed_balance() == funds
|
||||
@ -453,10 +453,10 @@ class TestWalletSimulator:
|
||||
assert fees == tx_fee
|
||||
|
||||
await wallet.push_transaction(tx)
|
||||
await time_out_assert(5, full_node_1.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
await time_out_assert(20, full_node_1.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds - tx_amount - tx_fee)
|
||||
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(20, wallet.get_unconfirmed_balance, funds - tx_amount - tx_fee)
|
||||
|
||||
for i in range(0, num_blocks):
|
||||
await full_node_1.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
||||
@ -468,8 +468,8 @@ class TestWalletSimulator:
|
||||
]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, new_funds - tx_amount - tx_fee)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, new_funds - tx_amount - tx_fee)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, new_funds - tx_amount - tx_fee)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, new_funds - tx_amount - tx_fee)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"trusted",
|
||||
@ -510,7 +510,7 @@ class TestWalletSimulator:
|
||||
[calculate_pool_reward(uint32(i)) + calculate_base_farmer_reward(uint32(i)) for i in range(1, num_blocks)]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(20, wallet.get_confirmed_balance, funds)
|
||||
|
||||
primaries: List[AmountWithPuzzlehash] = []
|
||||
for i in range(0, 60):
|
||||
@ -615,7 +615,7 @@ class TestWalletSimulator:
|
||||
)
|
||||
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds)
|
||||
|
||||
assert await wallet.get_confirmed_balance() == funds
|
||||
assert await wallet.get_unconfirmed_balance() == funds
|
||||
@ -658,8 +658,8 @@ class TestWalletSimulator:
|
||||
)
|
||||
await wallet.push_transaction(stolen_tx)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds - stolen_cs.coin.amount)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds - stolen_cs.coin.amount)
|
||||
|
||||
for i in range(0, num_blocks):
|
||||
await full_node_1.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
||||
@ -718,14 +718,14 @@ class TestWalletSimulator:
|
||||
assert tx.spend_bundle is not None
|
||||
await wallet.push_transaction(tx)
|
||||
await full_node_api.full_node.respond_transaction(tx.spend_bundle, tx.name)
|
||||
await time_out_assert(5, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
for i in range(0, 2):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32(32 * b"0")))
|
||||
await time_out_assert(5, wallet_2.get_confirmed_balance, 1000)
|
||||
await time_out_assert(10, wallet_2.get_confirmed_balance, 1000)
|
||||
funds -= 1000
|
||||
|
||||
await time_out_assert(5, wallet_node.wallet_state_manager.blockchain.get_peak_height, 7)
|
||||
await time_out_assert(10, wallet_node.wallet_state_manager.blockchain.get_peak_height, 7)
|
||||
peak = full_node_api.full_node.blockchain.get_peak()
|
||||
assert peak is not None
|
||||
peak_height = peak.height
|
||||
@ -743,8 +743,8 @@ class TestWalletSimulator:
|
||||
]
|
||||
)
|
||||
|
||||
await time_out_assert(7, full_node_api.full_node.blockchain.get_peak_height, peak_height + 3)
|
||||
await time_out_assert(7, wallet_node.wallet_state_manager.blockchain.get_peak_height, peak_height + 3)
|
||||
await time_out_assert(20, full_node_api.full_node.blockchain.get_peak_height, peak_height + 3)
|
||||
await time_out_assert(20, wallet_node.wallet_state_manager.blockchain.get_peak_height, peak_height + 3)
|
||||
|
||||
# Farm a few blocks so we can confirm the resubmitted transaction
|
||||
for i in range(0, num_blocks):
|
||||
@ -864,8 +864,8 @@ class TestWalletSimulator:
|
||||
]
|
||||
)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds)
|
||||
|
||||
AMOUNT_TO_SEND = 4000000000000
|
||||
coins = await wallet.select_coins(uint64(AMOUNT_TO_SEND))
|
||||
@ -883,12 +883,12 @@ class TestWalletSimulator:
|
||||
assert paid_coin.parent_coin_info == coin_list[2].name()
|
||||
await wallet.push_transaction(tx)
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds - AMOUNT_TO_SEND)
|
||||
await time_out_assert(5, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds - AMOUNT_TO_SEND)
|
||||
await time_out_assert(10, full_node_api.full_node.mempool_manager.get_spendbundle, tx.spend_bundle, tx.name)
|
||||
|
||||
for i in range(0, num_blocks):
|
||||
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(bytes32([0] * 32)))
|
||||
|
||||
await time_out_assert(5, wallet.get_confirmed_balance, funds - AMOUNT_TO_SEND)
|
||||
await time_out_assert(5, wallet.get_unconfirmed_balance, funds - AMOUNT_TO_SEND)
|
||||
await time_out_assert(10, wallet.get_confirmed_balance, funds - AMOUNT_TO_SEND)
|
||||
await time_out_assert(10, wallet.get_unconfirmed_balance, funds - AMOUNT_TO_SEND)
|
||||
|
Loading…
Reference in New Issue
Block a user