mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-11-29 05:18:11 +03:00
Ms.1.8bugs3 (#326)
* harvester fixes * Improve networking stability * Fix wallet shutdown * Allow chia keys sign and chia keys verify * Dislpay the public key also * Retry loading invalid plots, handle drive disconnection * Confirm before deleting plots * Improve error message WIP * XImproved error message for importing keys * Uncomment process.kill * Fix merge error with restore backup * Fixed markdown * Switch button order, and fix request_peers * Consolidate styles * Set ci's to timeout after 60 minutes has elapsed * plot directories and memory buffer * Fix flake8 * Update chiapos, chiavdf, chiabip158, and blspy Co-authored-by: Gene Hoffman <hoffmang@hoffmang.com> Co-authored-by: Gene Hoffman <30377676+hoffmang9@users.noreply.github.com>
This commit is contained in:
parent
3f6401d4c1
commit
700eaad9e0
1
.github/workflows/build-macos.yml
vendored
1
.github/workflows/build-macos.yml
vendored
@ -7,6 +7,7 @@ jobs:
|
||||
build:
|
||||
name: MacOS-latest on Pyton 3.7
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 4
|
||||
|
1
.github/workflows/build-ubuntu.yml
vendored
1
.github/workflows/build-ubuntu.yml
vendored
@ -7,6 +7,7 @@ jobs:
|
||||
build:
|
||||
name: Ubuntu-latest on Python 3.7 + 3.8
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 4
|
||||
|
1
.github/workflows/build-windows.yml
vendored
1
.github/workflows/build-windows.yml
vendored
@ -6,6 +6,7 @@ jobs:
|
||||
build:
|
||||
name: Windows installer on 3.7
|
||||
runs-on: [windows-latest]
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Cancel previous runs on the same branch
|
||||
|
1
.github/workflows/linter.yml
vendored
1
.github/workflows/linter.yml
vendored
@ -29,6 +29,7 @@ jobs:
|
||||
name: Lint Code Base
|
||||
# Set the agent to run on
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
|
||||
##################
|
||||
# Load all steps #
|
||||
|
1
.github/workflows/upload-pypi-source.yml
vendored
1
.github/workflows/upload-pypi-source.yml
vendored
@ -6,6 +6,7 @@ jobs:
|
||||
upload_source_dist:
|
||||
name: Lint and Upload source distribution
|
||||
runs-on: [ubuntu-latest]
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Cancel previous runs on the same branch
|
||||
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -9,18 +9,35 @@ for setuptools_scm/PEP 440 reasons.
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- See wallet balances in command line: chia show -w
|
||||
- Retry opening invalid plots every 20 minutes (so you can cp a large plot)
|
||||
- "chia keys sign" and "chia keys verify"
|
||||
- Windows BLS Signature library now uses libsodium for additional security.
|
||||
- Wheels for ARM64/aarch64 also build for python 3.7.
|
||||
- See and remove plot directories from UI and command line
|
||||
- Specify memory buffer in UI
|
||||
|
||||
### Changed
|
||||
- chia start wallet-server changed to chia start wallet, for consistency.
|
||||
- All data size units are displayed in GiB instead of GB (powers of 1024 instead of 1000)
|
||||
- Better error messages for restoring wallet from mnemonic
|
||||
|
||||
### Fixed
|
||||
- Fixed open_connection not being cancelled when node exits
|
||||
- Increase the robustness of node and wallet shutdown
|
||||
- Handle disconnection and reconnection of hard drives properly
|
||||
- Addressed pre-Haswell Windows signatures failing.
|
||||
- MacOS, Linux x64, and Linux aarch64 were not correctly compiling libsodium in
|
||||
the blspy/bls-signatures library.
|
||||
- Removed outdated "200 plots" language from Plot tab.
|
||||
- Fixed spelling error for "folder" on Plot tab.
|
||||
- Various node dependency security vulnerabilities have been fixed.
|
||||
- Request peers was not returning currently connected peers older than 1 day
|
||||
|
||||
### Deprecated
|
||||
- Removed legacy scripts such as chia-stop-server, chia-restart-harvester, etc.
|
||||
|
||||
|
||||
|
||||
## [1.0beta8] aka Beta 1.8 - 2020-07-16
|
||||
|
||||
|
@ -160,7 +160,7 @@ wallet_exe = EXE(wallet_pyz,
|
||||
wallet.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='wallet_server',
|
||||
name='start_wallet',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False)
|
||||
|
@ -163,7 +163,7 @@ wallet_exe = EXE(wallet_pyz,
|
||||
wallet.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='wallet_server',
|
||||
name='start_wallet',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False)
|
||||
|
@ -145,7 +145,7 @@ const closeDaemon = callback => {
|
||||
if (!called_cb) {
|
||||
callback();
|
||||
}
|
||||
}, 15000);
|
||||
}, 20000);
|
||||
};
|
||||
|
||||
const exitPyProc = e => {};
|
||||
@ -214,29 +214,17 @@ const createWindow = () => {
|
||||
// }
|
||||
mainWindow.on("close", e => {
|
||||
if (decidedToClose) {
|
||||
if (pyProc != null) {
|
||||
if (process.platform === "win32") {
|
||||
process.stdout.write("Killing daemon on windows");
|
||||
var cp = require("child_process");
|
||||
cp.execSync("taskkill /PID " + pyProc.pid + " /T /F");
|
||||
} else {
|
||||
process.stdout.write("Killing daemon on other platforms");
|
||||
pyProc.kill();
|
||||
pyProc = null;
|
||||
pyPort = null;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
var choice = require("electron").dialog.showMessageBoxSync({
|
||||
type: "question",
|
||||
buttons: ["Yes", "No"],
|
||||
buttons: ["No", "Yes"],
|
||||
title: "Confirm",
|
||||
message:
|
||||
"Are you sure you want to quit? GUI Plotting and farming will stop."
|
||||
});
|
||||
if (choice == 1) {
|
||||
if (choice == 0) {
|
||||
return;
|
||||
}
|
||||
mainWindow.loadURL(closingUrl);
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
} from "../modules/daemon_messages";
|
||||
import { handle_message } from "./middleware_api";
|
||||
import {
|
||||
service_wallet_server,
|
||||
service_wallet,
|
||||
service_full_node,
|
||||
service_simulator,
|
||||
service_plotter
|
||||
@ -38,10 +38,10 @@ const socketMiddleware = () => {
|
||||
|
||||
let start_wallet, start_node;
|
||||
if (config.local_test) {
|
||||
start_wallet = startServiceTest(service_wallet_server);
|
||||
start_wallet = startServiceTest(service_wallet);
|
||||
start_node = startService(service_simulator);
|
||||
} else {
|
||||
start_wallet = startService(service_wallet_server);
|
||||
start_wallet = startService(service_wallet);
|
||||
start_node = startService(service_full_node);
|
||||
}
|
||||
store.dispatch(isServiceRunning(service_plotter));
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { offerParsed, resetTrades } from "../modules/TradeReducer";
|
||||
import { openDialog } from "../modules/dialogReducer";
|
||||
import {
|
||||
service_wallet_server,
|
||||
service_wallet,
|
||||
service_full_node,
|
||||
service_simulator,
|
||||
service_farmer,
|
||||
@ -35,6 +35,7 @@ import {
|
||||
} from "../modules/farmerMessages";
|
||||
import {
|
||||
getPlots,
|
||||
getPlotDirectories,
|
||||
pingHarvester,
|
||||
refreshPlots
|
||||
} from "../modules/harvesterMessages";
|
||||
@ -55,7 +56,7 @@ function sleep(ms) {
|
||||
|
||||
async function ping_wallet(store) {
|
||||
store.dispatch(pingWallet());
|
||||
await sleep(300);
|
||||
await sleep(1000);
|
||||
const state = store.getState();
|
||||
const wallet_connected = state.daemon_state.wallet_connected;
|
||||
if (!wallet_connected) {
|
||||
@ -65,7 +66,7 @@ async function ping_wallet(store) {
|
||||
|
||||
async function ping_full_node(store) {
|
||||
store.dispatch(pingFullNode());
|
||||
await sleep(300);
|
||||
await sleep(1000);
|
||||
const state = store.getState();
|
||||
const node_connected = state.daemon_state.full_node_connected;
|
||||
if (!node_connected) {
|
||||
@ -75,7 +76,7 @@ async function ping_full_node(store) {
|
||||
|
||||
async function ping_farmer(store) {
|
||||
store.dispatch(pingFarmer());
|
||||
await sleep(300);
|
||||
await sleep(1000);
|
||||
const state = store.getState();
|
||||
const farmer_connected = state.daemon_state.farmer_connected;
|
||||
if (!farmer_connected) {
|
||||
@ -85,7 +86,7 @@ async function ping_farmer(store) {
|
||||
|
||||
async function ping_harvester(store) {
|
||||
store.dispatch(pingHarvester());
|
||||
await sleep(300);
|
||||
await sleep(1000);
|
||||
const state = store.getState();
|
||||
const harvester_connected = state.daemon_state.harvester_connected;
|
||||
if (!harvester_connected) {
|
||||
@ -145,6 +146,7 @@ export const refreshAllState = dispatch => {
|
||||
dispatch(getLatestChallenges());
|
||||
dispatch(getFarmerConnections());
|
||||
dispatch(getPlots());
|
||||
dispatch(getPlotDirectories());
|
||||
dispatch(isServiceRunning(service_plotter));
|
||||
dispatch(get_all_trades());
|
||||
};
|
||||
@ -152,7 +154,7 @@ export const refreshAllState = dispatch => {
|
||||
export const handle_message = (store, payload) => {
|
||||
store.dispatch(incomingMessage(payload));
|
||||
if (payload.command === "ping") {
|
||||
if (payload.origin === service_wallet_server) {
|
||||
if (payload.origin === service_wallet) {
|
||||
store.dispatch(get_connection_info());
|
||||
store.dispatch(format_message("get_public_keys", {}));
|
||||
} else if (payload.origin === service_full_node) {
|
||||
@ -163,7 +165,6 @@ export const handle_message = (store, payload) => {
|
||||
store.dispatch(getLatestChallenges());
|
||||
store.dispatch(getFarmerConnections());
|
||||
} else if (payload.origin === service_harvester) {
|
||||
store.dispatch(getPlots());
|
||||
}
|
||||
} else if (payload.command === "delete_key") {
|
||||
if (payload.data.success) {
|
||||
@ -244,7 +245,7 @@ export const handle_message = (store, payload) => {
|
||||
} else if (payload.command === "start_service") {
|
||||
const service = payload.data.service;
|
||||
if (payload.data.success) {
|
||||
if (service === service_wallet_server) {
|
||||
if (service === service_wallet) {
|
||||
ping_wallet(store);
|
||||
} else if (service === service_full_node) {
|
||||
ping_full_node(store);
|
||||
@ -258,7 +259,7 @@ export const handle_message = (store, payload) => {
|
||||
track_progress(store, payload.data.out_file);
|
||||
}
|
||||
} else if (payload.data.error === "already running") {
|
||||
if (service === service_wallet_server) {
|
||||
if (service === service_wallet) {
|
||||
ping_wallet(store);
|
||||
} else if (service === service_full_node) {
|
||||
ping_full_node(store);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { service_wallet_server } from "../util/service_names";
|
||||
import { service_wallet } from "../util/service_names";
|
||||
|
||||
export const addTrade = trade => ({ type: "TRADE_ADDED", trade });
|
||||
export const resetTrades = () => ({ type: "RESET_TRADE" });
|
||||
@ -53,7 +53,7 @@ export const tradeReducer = (state = { ...initial_state }, action) => {
|
||||
let trade;
|
||||
switch (action.type) {
|
||||
case "INCOMING_MESSAGE":
|
||||
if (action.message.origin !== service_wallet_server) {
|
||||
if (action.message.origin !== service_wallet) {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
service_wallet_server,
|
||||
service_wallet,
|
||||
service_full_node,
|
||||
service_simulator,
|
||||
service_daemon,
|
||||
@ -43,7 +43,7 @@ export const daemonReducer = (state = { ...initial_state }, action) => {
|
||||
state.full_node_running = true;
|
||||
} else if (service === service_simulator) {
|
||||
state.full_node_running = true;
|
||||
} else if (service === service_wallet_server) {
|
||||
} else if (service === service_wallet) {
|
||||
state.wallet_running = true;
|
||||
} else if (service === service_farmer) {
|
||||
state.farmer_running = true;
|
||||
@ -56,7 +56,7 @@ export const daemonReducer = (state = { ...initial_state }, action) => {
|
||||
state.full_node_connected = true;
|
||||
} else if (origin === service_simulator) {
|
||||
state.full_node_connected = true;
|
||||
} else if (origin === service_wallet_server) {
|
||||
} else if (origin === service_wallet) {
|
||||
state.wallet_connected = true;
|
||||
} else if (origin === service_farmer) {
|
||||
state.farmer_connected = true;
|
||||
@ -70,7 +70,7 @@ export const daemonReducer = (state = { ...initial_state }, action) => {
|
||||
state.plotter_running = data.is_running;
|
||||
} else if (service === service_full_node) {
|
||||
state.full_node_running = data.is_running;
|
||||
} else if (service === service_wallet_server) {
|
||||
} else if (service === service_wallet) {
|
||||
state.wallet_running = data.is_running;
|
||||
} else if (service === service_farmer) {
|
||||
state.farmer_running = data.is_running;
|
||||
|
@ -9,7 +9,8 @@ const initial_state = {
|
||||
harvester: {
|
||||
plots: [],
|
||||
not_found_filenames: [],
|
||||
failed_to_open_filenames: []
|
||||
failed_to_open_filenames: [],
|
||||
plot_directories: []
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,6 +70,14 @@ export const farmingReducer = (state = { ...initial_state }, action) => {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (command === "get_plot_directories") {
|
||||
if (data.success !== true) {
|
||||
return state;
|
||||
}
|
||||
state.harvester.plot_directories = data.directories;
|
||||
return state;
|
||||
}
|
||||
|
||||
return state;
|
||||
default:
|
||||
return state;
|
||||
|
@ -21,6 +21,13 @@ export const getPlots = () => {
|
||||
return action;
|
||||
};
|
||||
|
||||
export const getPlotDirectories = () => {
|
||||
var action = harvesterMessage();
|
||||
action.message.command = "get_plot_directories";
|
||||
action.message.data = {};
|
||||
return action;
|
||||
};
|
||||
|
||||
export const deletePlot = filename => {
|
||||
var action = harvesterMessage();
|
||||
action.message.command = "delete_plot";
|
||||
@ -41,3 +48,10 @@ export const addPlotDirectory = dirname => {
|
||||
action.message.data = { dirname };
|
||||
return action;
|
||||
};
|
||||
|
||||
export const removePlotDirectory = dirname => {
|
||||
var action = harvesterMessage();
|
||||
action.message.command = "remove_plot_directory";
|
||||
action.message.data = { dirname };
|
||||
return action;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { service_wallet_server } from "../util/service_names";
|
||||
import { service_wallet } from "../util/service_names";
|
||||
|
||||
export const Wallet = (id, name, type, data) => ({
|
||||
id: id,
|
||||
@ -101,7 +101,7 @@ export const incomingReducer = (state = { ...initial_state }, action) => {
|
||||
}
|
||||
return state;
|
||||
case "INCOMING_MESSAGE":
|
||||
if (action.message.origin !== service_wallet_server) {
|
||||
if (action.message.origin !== service_wallet) {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,21 @@
|
||||
import { service_wallet_server } from "../util/service_names";
|
||||
import { service_wallet } from "../util/service_names";
|
||||
import { openProgress, closeProgress } from "./progressReducer";
|
||||
import { refreshAllState } from "../middleware/middleware_api";
|
||||
import { changeEntranceMenu, presentRestoreBackup } from "./entranceMenu";
|
||||
import { setIncorrectWord, resetMnemonic } from "./mnemonic_input";
|
||||
import {
|
||||
changeEntranceMenu,
|
||||
presentRestoreBackup,
|
||||
presentOldWallet
|
||||
} from "./entranceMenu";
|
||||
import { openDialog } from "./dialogReducer";
|
||||
import { createState } from "./createWalletReducer";
|
||||
import {
|
||||
addPlotDirectory,
|
||||
getPlotDirectories,
|
||||
removePlotDirectory,
|
||||
getPlots,
|
||||
refreshPlots
|
||||
} from "./harvesterMessages";
|
||||
|
||||
export const clearSend = () => {
|
||||
var action = {
|
||||
@ -16,7 +28,7 @@ export const clearSend = () => {
|
||||
export const walletMessage = () => ({
|
||||
type: "OUTGOING_MESSAGE",
|
||||
message: {
|
||||
destination: service_wallet_server
|
||||
destination: service_wallet
|
||||
}
|
||||
});
|
||||
|
||||
@ -116,9 +128,16 @@ export const add_new_key_action = mnemonic => {
|
||||
dispatch(closeProgress());
|
||||
if (response.data.success) {
|
||||
// Go to wallet
|
||||
dispatch(resetMnemonic());
|
||||
dispatch(format_message("get_public_keys", {}));
|
||||
refreshAllState(dispatch);
|
||||
} else {
|
||||
if (response.data.word) {
|
||||
dispatch(setIncorrectWord(response.data.word));
|
||||
dispatch(changeEntranceMenu(presentOldWallet));
|
||||
} else if (response.data.error === "Invalid order of mnemonic words") {
|
||||
dispatch(changeEntranceMenu(presentOldWallet));
|
||||
}
|
||||
const error = response.data.error;
|
||||
dispatch(openDialog("Error", error));
|
||||
}
|
||||
@ -133,9 +152,18 @@ export const add_and_skip_backup = mnemonic => {
|
||||
dispatch(closeProgress());
|
||||
if (response.data.success) {
|
||||
// Go to wallet
|
||||
dispatch(resetMnemonic());
|
||||
dispatch(format_message("get_public_keys", {}));
|
||||
refreshAllState(dispatch);
|
||||
} else {
|
||||
if (response.data.word) {
|
||||
dispatch(setIncorrectWord(response.data.word));
|
||||
dispatch(changeEntranceMenu(presentOldWallet));
|
||||
} else if (
|
||||
response.data.error === "Invalid order of mnemonic words"
|
||||
) {
|
||||
dispatch(changeEntranceMenu(presentOldWallet));
|
||||
}
|
||||
const error = response.data.error;
|
||||
dispatch(openDialog("Error", error));
|
||||
}
|
||||
@ -154,9 +182,16 @@ export const add_and_restore_from_backup = (mnemonic, file_path) => {
|
||||
dispatch(closeProgress());
|
||||
if (response.data.success) {
|
||||
// Go to wallet
|
||||
dispatch(resetMnemonic());
|
||||
dispatch(format_message("get_public_keys", {}));
|
||||
refreshAllState(dispatch);
|
||||
} else {
|
||||
if (response.data.word) {
|
||||
dispatch(setIncorrectWord(response.data.word));
|
||||
dispatch(changeEntranceMenu(presentOldWallet));
|
||||
} else if (response.data.error === "Invalid order of mnemonic words") {
|
||||
dispatch(changeEntranceMenu(presentOldWallet));
|
||||
}
|
||||
const error = response.data.error;
|
||||
dispatch(openDialog("Error", error));
|
||||
}
|
||||
@ -423,3 +458,41 @@ export const incomingMessage = message => ({
|
||||
type: "INCOMING_MESSAGE",
|
||||
message: message
|
||||
});
|
||||
|
||||
export const add_plot_directory_and_refresh = dir => {
|
||||
return dispatch => {
|
||||
return async_api(dispatch, addPlotDirectory(dir), true).then(response => {
|
||||
if (response.data.success) {
|
||||
dispatch(getPlotDirectories());
|
||||
return async_api(dispatch, refreshPlots(dir), false).then(response => {
|
||||
dispatch(closeProgress());
|
||||
dispatch(getPlots());
|
||||
});
|
||||
} else {
|
||||
const error = response.data.error;
|
||||
dispatch(openDialog("Error", error));
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const remove_plot_directory_and_refresh = dir => {
|
||||
return dispatch => {
|
||||
return async_api(dispatch, removePlotDirectory(dir), true).then(
|
||||
response => {
|
||||
if (response.data.success) {
|
||||
dispatch(getPlotDirectories());
|
||||
return async_api(dispatch, refreshPlots(dir), false).then(
|
||||
response => {
|
||||
dispatch(closeProgress());
|
||||
dispatch(getPlots());
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const error = response.data.error;
|
||||
dispatch(openDialog("Error", error));
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
export const wordChanged = msg => ({ type: "MNEMONIC_TYPING" });
|
||||
export const resetMnemonic = msg => ({ type: "RESET_MNEMONIC" });
|
||||
export const setIncorrectWord = word => ({ type: "SET_INCORRECT_WORD", word });
|
||||
|
||||
const initial_state = {
|
||||
mnemonic_input: new Array("24"),
|
||||
selected_mnemonic: null
|
||||
mnemonic_input: new Array(24).fill(""),
|
||||
incorrect_word: null
|
||||
};
|
||||
|
||||
export const mnemonic_word_added = data => {
|
||||
@ -18,13 +19,15 @@ export const mnemonicReducer = (state = { ...initial_state }, action) => {
|
||||
var word = action.data.word;
|
||||
var id = action.data.id;
|
||||
var current_input = state.mnemonic_input;
|
||||
console.log(state.mnemonic_input);
|
||||
current_input[id] = word;
|
||||
return { ...state, mnemonic_input: current_input };
|
||||
case "RESET_MNEMONIC":
|
||||
return { ...initial_state };
|
||||
case "SELECT_MNEMONIC":
|
||||
return { ...state, selected_mnemonic: action.mnemonic };
|
||||
return {
|
||||
mnemonic_input: new Array(24).fill(""),
|
||||
incorrect_word: null
|
||||
};
|
||||
case "SET_INCORRECT_WORD":
|
||||
return { ...state, incorrect_word: action.word };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
const initial_state = {
|
||||
plotting_in_proggress: false,
|
||||
workspace_location: "",
|
||||
t2: "",
|
||||
final_location: "",
|
||||
progress_location: "",
|
||||
progress: "",
|
||||
|
@ -5,16 +5,17 @@ export const plotControl = () => ({
|
||||
type: "PLOTTER_CONTROL"
|
||||
});
|
||||
|
||||
export const startPlotting = (size, count, workspace, final) => {
|
||||
export const startPlotting = (k, n, t, t2, d, b) => {
|
||||
var action = daemonMessage();
|
||||
action.message.command = "start_plotting";
|
||||
action.message.data = {
|
||||
service: service_plotter,
|
||||
k: size,
|
||||
n: count,
|
||||
t: workspace,
|
||||
t2: workspace,
|
||||
d: final
|
||||
k,
|
||||
n,
|
||||
t,
|
||||
t2,
|
||||
d,
|
||||
b
|
||||
};
|
||||
return action;
|
||||
};
|
||||
|
@ -7,21 +7,37 @@ import isElectron from "is-electron";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import React from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import { TextField } from "@material-ui/core";
|
||||
import { useDispatch } from "react-redux";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemAvatar from "@material-ui/core/ListItemAvatar";
|
||||
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import Avatar from "@material-ui/core/Avatar";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import FolderIcon from "@material-ui/icons/Folder";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
|
||||
import {
|
||||
add_plot_directory_and_refresh,
|
||||
remove_plot_directory_and_refresh
|
||||
} from "../modules/message";
|
||||
|
||||
const styles = theme => ({
|
||||
dialogTitle: {
|
||||
width: 500
|
||||
},
|
||||
addPlotButton: {
|
||||
width: 80,
|
||||
width: 220,
|
||||
marginLeft: theme.spacing(2),
|
||||
height: 56
|
||||
},
|
||||
keyInput: {
|
||||
marginTop: 10
|
||||
},
|
||||
dirList: {
|
||||
width: "100%"
|
||||
}
|
||||
});
|
||||
|
||||
@ -30,46 +46,40 @@ const useStyles = makeStyles(styles);
|
||||
function AddPlotDialog(props) {
|
||||
const classes = useStyles();
|
||||
const { onClose, open, ...other } = props;
|
||||
const [plotDir, setPlotDir] = React.useState("");
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleEntering = () => {};
|
||||
const directories = useSelector(
|
||||
state => state.farming_state.harvester.plot_directories
|
||||
);
|
||||
|
||||
const handleCancel = () => {
|
||||
onClose({});
|
||||
setPlotDir("");
|
||||
const removePlotDir = dir => {
|
||||
dispatch(remove_plot_directory_and_refresh(dir));
|
||||
};
|
||||
|
||||
const handleOk = () => {
|
||||
onClose(plotDir);
|
||||
setPlotDir("");
|
||||
};
|
||||
|
||||
async function select(setterFn) {
|
||||
async function select() {
|
||||
if (isElectron()) {
|
||||
const dialogOptions = {
|
||||
properties: ["openDirectory", "showHiddenFiles"],
|
||||
buttonLabel: "Select Plot Directory"
|
||||
};
|
||||
const result = await window.remote.dialog.showOpenDialog(dialogOptions);
|
||||
const filePath = result["filePaths"][0];
|
||||
setterFn(filePath);
|
||||
console.log(result);
|
||||
if (!result.canceled) {
|
||||
const filePath = result["filePaths"][0];
|
||||
dispatch(add_plot_directory_and_refresh(filePath));
|
||||
}
|
||||
} else {
|
||||
dispatch(
|
||||
openDialog("", "This feature is available only from electron app")
|
||||
);
|
||||
}
|
||||
}
|
||||
async function selectPlot() {
|
||||
select(setPlotDir);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
disableBackdropClick
|
||||
disableEscapeKeyDown
|
||||
maxWidth="md"
|
||||
onEntering={handleEntering}
|
||||
aria-labelledby="confirmation-dialog-title"
|
||||
open={open}
|
||||
{...other}
|
||||
@ -86,33 +96,44 @@ function AddPlotDialog(props) {
|
||||
not created any plots, go to the plotting screen.
|
||||
</p>
|
||||
<Box display="flex">
|
||||
<Box flexGrow={1}>
|
||||
<TextField
|
||||
disabled
|
||||
className={classes.input}
|
||||
fullWidth
|
||||
label={plotDir === "" ? "Plot directory" : plotDir}
|
||||
variant="outlined"
|
||||
/>
|
||||
</Box>
|
||||
<List dense={true} className={classes.dirList}>
|
||||
{directories.map(dir => (
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar>
|
||||
<FolderIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={dir} />
|
||||
<ListItemSecondaryAction>
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="delete"
|
||||
onClick={() => removePlotDir(dir)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
<Box display="flex">
|
||||
<Box>
|
||||
<Button
|
||||
onClick={selectPlot}
|
||||
onClick={select}
|
||||
className={classes.addPlotButton}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
Select
|
||||
Add plot directory
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button autoFocus onClick={handleCancel} color="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleOk} color="secondary">
|
||||
Ok
|
||||
<Button autoFocus onClick={onClose} color="secondary">
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
@ -119,7 +119,7 @@ const Connections = props => {
|
||||
|
||||
<TableCell align="right">
|
||||
{Math.floor(item.bytes_written / 1024)}/
|
||||
{Math.floor(item.bytes_read / 1024)} KB
|
||||
{Math.floor(item.bytes_read / 1024)} KiB
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{service_connection_types[item.type]}
|
||||
|
@ -22,12 +22,17 @@ import TableHead from "@material-ui/core/TableHead";
|
||||
import DeleteForeverIcon from "@material-ui/icons/DeleteForever";
|
||||
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
|
||||
import { closeConnection, openConnection } from "../modules/farmerMessages";
|
||||
import {
|
||||
refreshPlots,
|
||||
deletePlot,
|
||||
addPlotDirectory
|
||||
getPlotDirectories
|
||||
} from "../modules/harvesterMessages";
|
||||
|
||||
import TablePagination from "@material-ui/core/TablePagination";
|
||||
@ -135,7 +140,7 @@ const getStatusItems = (
|
||||
|
||||
status_items.push({
|
||||
label: "Total size of local plots",
|
||||
value: Math.floor(farmerSpace / Math.pow(1024, 3)).toString() + " GB",
|
||||
value: Math.floor(farmerSpace / Math.pow(1024, 3)).toString() + " GiB",
|
||||
tooltip:
|
||||
"You have " +
|
||||
(proportion * 100).toFixed(6) +
|
||||
@ -305,7 +310,9 @@ const Plots = props => {
|
||||
plots.sort((a, b) => b.size - a.size);
|
||||
const [page, setPage] = React.useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(10);
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [addDirectoryOpen, addDirectorySetOpen] = React.useState(false);
|
||||
const [deletePlotName, deletePlotSetName] = React.useState("");
|
||||
const [deletePlotOpen, deletePlotSetOpen] = React.useState(false);
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
@ -316,20 +323,21 @@ const Plots = props => {
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
const deletePlotClick = filename => {
|
||||
return () => {
|
||||
dispatch(deletePlot(filename));
|
||||
};
|
||||
};
|
||||
|
||||
const refreshPlotsClick = () => {
|
||||
dispatch(refreshPlots());
|
||||
};
|
||||
const handleClose = response => {
|
||||
setOpen(false);
|
||||
if (response) {
|
||||
dispatch(addPlotDirectory(response));
|
||||
}
|
||||
const addDirectoryHandleClose = () => {
|
||||
addDirectorySetOpen(false);
|
||||
};
|
||||
|
||||
const handleCloseDeletePlot = () => {
|
||||
deletePlotSetOpen(false);
|
||||
};
|
||||
|
||||
const handleCloseDeletePlotYes = () => {
|
||||
handleCloseDeletePlot();
|
||||
console.log("deleting plot", deletePlotName);
|
||||
dispatch(deletePlot(deletePlotName));
|
||||
};
|
||||
|
||||
return (
|
||||
@ -353,10 +361,11 @@ const Plots = props => {
|
||||
color="primary"
|
||||
className={classes.addPlotButton}
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
dispatch(getPlotDirectories());
|
||||
addDirectorySetOpen(true);
|
||||
}}
|
||||
>
|
||||
Add plots
|
||||
Manage plot directories
|
||||
</Button>
|
||||
<AddPlotDialog
|
||||
classes={{
|
||||
@ -364,8 +373,8 @@ const Plots = props => {
|
||||
}}
|
||||
id="ringtone-menu"
|
||||
keepMounted
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
open={addDirectoryOpen}
|
||||
onClose={addDirectoryHandleClose}
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
@ -401,7 +410,7 @@ const Plots = props => {
|
||||
{Math.round(
|
||||
(item.file_size * 1000) / (1024 * 1024 * 1024)
|
||||
) / 1000}
|
||||
GB)
|
||||
GiB)
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Tooltip title={item["plot-seed"]} interactive>
|
||||
@ -424,7 +433,10 @@ const Plots = props => {
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={classes.clickable}
|
||||
onClick={deletePlotClick(item.filename)}
|
||||
onClick={() => {
|
||||
deletePlotSetName(item.filename);
|
||||
deletePlotSetOpen(true);
|
||||
}}
|
||||
align="right"
|
||||
>
|
||||
<DeleteForeverIcon fontSize="small"></DeleteForeverIcon>
|
||||
@ -463,7 +475,10 @@ const Plots = props => {
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="delete"
|
||||
onClick={deletePlotClick(filename)}
|
||||
onClick={() => {
|
||||
deletePlotSetName(filename);
|
||||
deletePlotSetOpen(true);
|
||||
}}
|
||||
>
|
||||
<DeleteForeverIcon />
|
||||
</IconButton>
|
||||
@ -493,7 +508,10 @@ const Plots = props => {
|
||||
<IconButton
|
||||
edge="end"
|
||||
aria-label="delete"
|
||||
onClick={deletePlotClick(filename)}
|
||||
onClick={() => {
|
||||
deletePlotSetName(filename);
|
||||
deletePlotSetOpen(true);
|
||||
}}
|
||||
>
|
||||
<DeleteForeverIcon />
|
||||
</IconButton>
|
||||
@ -507,6 +525,32 @@ const Plots = props => {
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Dialog
|
||||
open={deletePlotOpen}
|
||||
onClose={handleCloseDeletePlot}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Delete all keys"}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete the plot? The plot cannot be
|
||||
recovered.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDeletePlot} color="secondary">
|
||||
Back
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCloseDeletePlotYes}
|
||||
color="secondary"
|
||||
autoFocus
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
@ -256,7 +256,7 @@ const getStatusItems = (state, connected) => {
|
||||
status_items.push(min_item);
|
||||
|
||||
const space =
|
||||
(BigInt(state.space) / BigInt(Math.pow(1024, 4))).toString() + "TB";
|
||||
(BigInt(state.space) / BigInt(Math.pow(1024, 4))).toString() + "TiB";
|
||||
const space_item = {
|
||||
label: "Estimated network space",
|
||||
value: space,
|
||||
|
@ -1,8 +1,7 @@
|
||||
import React, { Component, useEffect } from "react";
|
||||
import React, { Component } from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { withTheme } from "@material-ui/styles";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import ArrowBackIosIcon from "@material-ui/icons/ArrowBackIos";
|
||||
@ -10,9 +9,9 @@ import { connect, useSelector, useDispatch } from "react-redux";
|
||||
import { genereate_mnemonics, add_new_key_action } from "../modules/message";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import CssTextField from "../components/cssTextField";
|
||||
import myStyle from "./style";
|
||||
import { changeEntranceMenu, presentSelectKeys } from "../modules/entranceMenu";
|
||||
import { openDialog } from "../modules/dialogReducer";
|
||||
import logo from "../assets/img/chia_logo.svg";
|
||||
import myStyle from "./style";
|
||||
|
||||
const MnemonicField = props => {
|
||||
return (
|
||||
@ -48,17 +47,6 @@ const UIPart = props => {
|
||||
words = [];
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
openDialog(
|
||||
"Welcome!",
|
||||
`The following words are used for your wallet backup.
|
||||
Without them, you will lose access to your wallet, keep them safe!!!
|
||||
Write down each word along with the order number next to them. (Order is important) `
|
||||
)
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
function goBack() {
|
||||
dispatch(changeEntranceMenu(presentSelectKeys));
|
||||
}
|
||||
@ -73,10 +61,15 @@ const UIPart = props => {
|
||||
{" "}
|
||||
</ArrowBackIosIcon>
|
||||
<div className={classes.grid_wrap}>
|
||||
<img className={classes.logo} src={logo} alt="Logo" />
|
||||
<Container className={classes.grid} maxWidth="lg">
|
||||
<Typography className={classes.title} component="h4" variant="h4">
|
||||
New Wallet
|
||||
</Typography>
|
||||
<h1 className={classes.titleSmallMargin}>New Wallet</h1>
|
||||
<p className={classes.whiteP}>
|
||||
Welcome! The following words are used for your wallet backup.
|
||||
Without them, you will lose access to your wallet, keep them safe!
|
||||
Write down each word along with the order number next to them.
|
||||
(Order is important)
|
||||
</p>
|
||||
<Grid container spacing={2}>
|
||||
<Iterator mnemonic={words}></Iterator>
|
||||
</Grid>
|
||||
|
@ -1,17 +1,15 @@
|
||||
import React, { Component, useEffect } from "react";
|
||||
import React, { Component } from "react";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import Link from "@material-ui/core/Link";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { withTheme } from "@material-ui/styles";
|
||||
import Container from "@material-ui/core/Container";
|
||||
import ArrowBackIosIcon from "@material-ui/icons/ArrowBackIos";
|
||||
import { connect, useSelector } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import CssTextField from "../components/cssTextField";
|
||||
import myStyle from "./style";
|
||||
import { useStore, useDispatch } from "react-redux";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { mnemonic_word_added, resetMnemonic } from "../modules/mnemonic_input";
|
||||
import { unselectFingerprint } from "../modules/message";
|
||||
import {
|
||||
@ -19,7 +17,8 @@ import {
|
||||
presentSelectKeys,
|
||||
presentRestoreBackup
|
||||
} from "../modules/entranceMenu";
|
||||
import { openDialog } from "../modules/dialogReducer";
|
||||
import logo from "../assets/img/chia_logo.svg";
|
||||
import myStyle from "./style";
|
||||
|
||||
const MnemonicField = props => {
|
||||
return (
|
||||
@ -32,8 +31,9 @@ const MnemonicField = props => {
|
||||
color="primary"
|
||||
id={props.id}
|
||||
label={props.index}
|
||||
error={props.error}
|
||||
autoFocus={props.autofocus}
|
||||
defaultValue=""
|
||||
defaultValue={props.value}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
</Grid>
|
||||
@ -41,12 +41,13 @@ const MnemonicField = props => {
|
||||
};
|
||||
|
||||
const Iterator = props => {
|
||||
const store = useStore();
|
||||
const dispatch = useDispatch();
|
||||
const mnemonic_state = useSelector(state => state.mnemonic_state);
|
||||
const incorrect_word = useSelector(
|
||||
state => state.mnemonic_state.incorrect_word
|
||||
);
|
||||
|
||||
function handleTextFieldChange(e) {
|
||||
console.log(e.target);
|
||||
console.log(store);
|
||||
var id = e.target.id + "";
|
||||
var clean_id = id.replace("id_", "");
|
||||
var int_val = parseInt(clean_id) - 1;
|
||||
@ -60,6 +61,11 @@ const Iterator = props => {
|
||||
<MnemonicField
|
||||
onChange={handleTextFieldChange}
|
||||
key={i}
|
||||
error={
|
||||
(props.submitted && mnemonic_state.mnemonic_input[i] === "") ||
|
||||
mnemonic_state.mnemonic_input[i] === incorrect_word
|
||||
}
|
||||
value={mnemonic_state.mnemonic_input[i]}
|
||||
autofocus={focus}
|
||||
id={"id_" + (i + 1)}
|
||||
index={i + 1}
|
||||
@ -69,42 +75,44 @@ const Iterator = props => {
|
||||
return indents;
|
||||
};
|
||||
|
||||
const UIPart = props => {
|
||||
const UIPart = () => {
|
||||
function goBack() {
|
||||
dispatch(resetMnemonic());
|
||||
dispatch(changeEntranceMenu(presentSelectKeys));
|
||||
}
|
||||
const dispatch = useDispatch();
|
||||
const [submitted, setSubmitted] = React.useState(false);
|
||||
const mnemonic = useSelector(state => state.mnemonic_state.mnemonic_input);
|
||||
const classes = myStyle();
|
||||
|
||||
function enterMnemonic() {
|
||||
setSubmitted(true);
|
||||
for (var i = 0; i < mnemonic.length; i++) {
|
||||
if (mnemonic[i] === "") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
dispatch(unselectFingerprint());
|
||||
dispatch(changeEntranceMenu(presentRestoreBackup));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
openDialog(
|
||||
"Welcome!",
|
||||
`Enter the 24 word mmemonic that you have saved in order to restore your Chia wallet. `
|
||||
)
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
const words = useSelector(state => state.wallet_state.mnemonic);
|
||||
const classes = myStyle();
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Link onClick={goBack} href="#">
|
||||
<ArrowBackIosIcon className={classes.navigator}> </ArrowBackIosIcon>
|
||||
</Link>
|
||||
<div className={classes.grid_wrap}>
|
||||
<img className={classes.logo} src={logo} alt="Logo" />
|
||||
<Container className={classes.grid} maxWidth="lg">
|
||||
<Typography className={classes.title} component="h4" variant="h4">
|
||||
<h1 className={classes.titleSmallMargin}>
|
||||
Import Wallet from Mnemonics
|
||||
</Typography>
|
||||
</h1>
|
||||
<p className={classes.whiteP}>
|
||||
Enter the 24 word mmemonic that you have saved in order to restore
|
||||
your Chia wallet.
|
||||
</p>
|
||||
<Grid container spacing={2}>
|
||||
<Iterator mnemonic={words}></Iterator>
|
||||
<Iterator submitted={submitted}></Iterator>
|
||||
</Grid>
|
||||
</Container>
|
||||
</div>
|
||||
@ -134,10 +142,6 @@ class OldWallet extends Component {
|
||||
this.classes = props.theme;
|
||||
}
|
||||
|
||||
componentDidMount(props) {
|
||||
console.log("Input Mnemonic");
|
||||
}
|
||||
|
||||
render() {
|
||||
return <UIPart props={this.props}></UIPart>;
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
} from "@material-ui/core";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import FormHelperText from "@material-ui/core/FormHelperText";
|
||||
import { openDialog } from "../modules/dialogReducer";
|
||||
import isElectron from "is-electron";
|
||||
import {
|
||||
@ -25,6 +26,8 @@ import {
|
||||
} from "../modules/plotter_messages";
|
||||
import { stopService } from "../modules/daemon_messages";
|
||||
import { service_plotter } from "../util/service_names";
|
||||
import Input from "@material-ui/core/Input";
|
||||
|
||||
const drawerWidth = 180;
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
@ -190,18 +193,18 @@ const useStyles = makeStyles(theme => ({
|
||||
}));
|
||||
|
||||
const plot_size_options = [
|
||||
// { label: "60MB", value: 16, workspace: "3.5GB" },
|
||||
{ label: "600MB", value: 25, workspace: "3.5GB" },
|
||||
{ label: "1.3GB", value: 26, workspace: "7GB" },
|
||||
{ label: "2.7GB", value: 27, workspace: "14.5GB" },
|
||||
{ label: "5.6GB", value: 28, workspace: "30.3GB" },
|
||||
{ label: "11.5GB", value: 29, workspace: "61GB" },
|
||||
{ label: "23.8GB", value: 30, workspace: "128GB" },
|
||||
{ label: "49.1GB", value: 31, workspace: "262GB" },
|
||||
{ label: "101.4GB", value: 32, workspace: "566GB" },
|
||||
{ label: "208.8GB", value: 33, workspace: "1095GB" },
|
||||
{ label: "429.8GB", value: 34, workspace: "2287GB" },
|
||||
{ label: "884.1GB", value: 35, workspace: "4672GB" }
|
||||
// { label: "60MiB", value: 16, workspace: "3.5GiB" },
|
||||
{ label: "600MiB", value: 25, workspace: "3.5GiB" },
|
||||
{ label: "1.3GiB", value: 26, workspace: "7GiB" },
|
||||
{ label: "2.7GiB", value: 27, workspace: "14.5GiB" },
|
||||
{ label: "5.6GiB", value: 28, workspace: "30.3GiB" },
|
||||
{ label: "11.5GiB", value: 29, workspace: "61GiB" },
|
||||
{ label: "23.8GiB", value: 30, workspace: "128GiB" },
|
||||
{ label: "49.1GiB", value: 31, workspace: "262GiB" },
|
||||
{ label: "101.4GiB", value: 32, workspace: "566GiB" },
|
||||
{ label: "208.8GiB", value: 33, workspace: "1095GiB" },
|
||||
{ label: "429.8GiB", value: 34, workspace: "2287GiB" },
|
||||
{ label: "884.1GiB", value: 35, workspace: "4672GiB" }
|
||||
];
|
||||
|
||||
const WorkLocation = () => {
|
||||
@ -322,11 +325,13 @@ const CreatePlot = () => {
|
||||
const work_location = useSelector(
|
||||
state => state.plot_control.workspace_location
|
||||
);
|
||||
let t2 = useSelector(state => state.plot_control.t2);
|
||||
const final_location = useSelector(
|
||||
state => state.plot_control.final_location
|
||||
);
|
||||
const [plotSize, setPlotSize] = React.useState(25);
|
||||
const [plotCount, setPlotCount] = React.useState(1);
|
||||
const [maxRam, setMaxRam] = React.useState(2000);
|
||||
|
||||
const changePlotSize = event => {
|
||||
setPlotSize(event.target.value);
|
||||
@ -334,6 +339,9 @@ const CreatePlot = () => {
|
||||
const changePlotCount = event => {
|
||||
setPlotCount(event.target.value);
|
||||
};
|
||||
const handleSetMaxRam = event => {
|
||||
setMaxRam(event.target.value);
|
||||
};
|
||||
|
||||
function create() {
|
||||
if (!work_location || !final_location) {
|
||||
@ -342,7 +350,10 @@ const CreatePlot = () => {
|
||||
}
|
||||
const N = plotCount;
|
||||
const K = plotSize;
|
||||
dispatch(startPlotting(K, N, work_location, final_location));
|
||||
if (!t2 || t2 === "") {
|
||||
t2 = final_location;
|
||||
}
|
||||
dispatch(startPlotting(K, N, work_location, t2, final_location, maxRam));
|
||||
}
|
||||
|
||||
var plot_count_options = [];
|
||||
@ -374,7 +385,7 @@ const CreatePlot = () => {
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.cardSubSection}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Grid item xs={4}>
|
||||
<FormControl
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
@ -398,7 +409,7 @@ const CreatePlot = () => {
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Grid item xs={4}>
|
||||
<FormControl
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
@ -418,6 +429,27 @@ const CreatePlot = () => {
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<FormControl
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
className={classes.formControl}
|
||||
>
|
||||
<InputLabel>RAM max usage</InputLabel>
|
||||
<Input
|
||||
value={maxRam}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">MiB</InputAdornment>
|
||||
}
|
||||
onChange={handleSetMaxRam}
|
||||
label="Colour"
|
||||
type="number"
|
||||
/>
|
||||
<FormHelperText id="standard-weight-helper-text">
|
||||
More memory slightly increases speed
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Grid>
|
||||
|
@ -191,7 +191,7 @@ const SelectKey = () => {
|
||||
<div className={classes.paper}>
|
||||
<img className={classes.logo} src={logo} alt="Logo" />
|
||||
{public_key_fingerprints && public_key_fingerprints.length > 0 ? (
|
||||
<h2 className={classes.whiteText}>Select Key</h2>
|
||||
<h1 className={classes.whiteText}>Select Key</h1>
|
||||
) : (
|
||||
<span className={classes.centeredSpan}>
|
||||
<h2 className={classes.whiteText}>Sign In</h2>
|
||||
|
@ -26,8 +26,11 @@ const UIPart = props => {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const classes = myStyle();
|
||||
if (words.length === 0) {
|
||||
words = null;
|
||||
|
||||
for (let word of words) {
|
||||
if (word === "") {
|
||||
words = null;
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
@ -60,10 +63,8 @@ const UIPart = props => {
|
||||
|
||||
const file_path = e.dataTransfer.files[0].path;
|
||||
if (fingerprint !== null) {
|
||||
debugger;
|
||||
dispatch(log_in_and_import_backup(fingerprint, file_path));
|
||||
} else if (words !== null) {
|
||||
debugger;
|
||||
dispatch(add_and_restore_from_backup(words, file_path));
|
||||
}
|
||||
};
|
||||
|
@ -28,7 +28,8 @@ const myStyle = makeStyles(theme => ({
|
||||
},
|
||||
grid_wrap: {
|
||||
paddingLeft: theme.spacing(10),
|
||||
paddingRight: theme.spacing(10)
|
||||
paddingRight: theme.spacing(10),
|
||||
textAlign: "center"
|
||||
},
|
||||
grid: {
|
||||
display: "flex",
|
||||
@ -50,6 +51,11 @@ const myStyle = makeStyles(theme => ({
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(8)
|
||||
},
|
||||
titleSmallMargin: {
|
||||
color: "#ffffff",
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(2)
|
||||
},
|
||||
navigator: {
|
||||
color: "#ffffff",
|
||||
marginTop: theme.spacing(4),
|
||||
@ -84,6 +90,14 @@ const myStyle = makeStyles(theme => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
},
|
||||
logo: {
|
||||
marginTop: theme.spacing(0),
|
||||
marginBottom: theme.spacing(1)
|
||||
},
|
||||
whiteP: {
|
||||
color: "white",
|
||||
fontSize: "18px"
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const service_wallet_server = "chia-wallet";
|
||||
export const service_wallet = "chia_wallet";
|
||||
export const service_full_node = "chia_full_node";
|
||||
export const service_farmer = "chia_farmer";
|
||||
export const service_harvester = "chia_harvester";
|
||||
|
@ -1,26 +0,0 @@
|
||||
_kill_servers() {
|
||||
PROCS=`ps -e | grep -E 'chia|vdf_client' -v "chia-start-sim" | awk '!/grep/' | awk '{print $1}'`
|
||||
if [ -n "$PROCS" ]; then
|
||||
echo "$PROCS" | xargs -L1 kill -KILL
|
||||
fi
|
||||
}
|
||||
|
||||
_kill_servers
|
||||
|
||||
BG_PIDS=""
|
||||
_run_bg_cmd() {
|
||||
"$@" &
|
||||
BG_PIDS="$BG_PIDS $!"
|
||||
}
|
||||
|
||||
|
||||
_term() {
|
||||
echo "Caught TERM or INT signal, killing all servers."
|
||||
for PID in $BG_PIDS; do
|
||||
kill -TERM "$PID"
|
||||
done
|
||||
_kill_servers
|
||||
}
|
||||
|
||||
trap _term TERM
|
||||
trap _term INT
|
@ -1,26 +0,0 @@
|
||||
_kill_servers() {
|
||||
PROCS=`ps -e | grep -E 'chia-wallet' | awk '!/grep/' | awk '{print $1}'`
|
||||
if [ -n "$PROCS" ]; then
|
||||
echo "$PROCS" | xargs -L1 kill -KILL
|
||||
fi
|
||||
}
|
||||
|
||||
_kill_servers
|
||||
|
||||
BG_PIDS=""
|
||||
_run_bg_cmd() {
|
||||
"$@" &
|
||||
BG_PIDS="$BG_PIDS $!"
|
||||
}
|
||||
|
||||
|
||||
_term() {
|
||||
echo "Caught TERM or INT signal, killing all servers."
|
||||
for PID in $BG_PIDS; do
|
||||
kill -TERM "$PID"
|
||||
done
|
||||
_kill_servers
|
||||
}
|
||||
|
||||
trap _term TERM
|
||||
trap _term INT
|
@ -1,2 +0,0 @@
|
||||
echo "Sorry, this script is broken."
|
||||
echo "You must manually remove files from ~/.chia/*/db/ for now."
|
@ -1,31 +0,0 @@
|
||||
_restart_harvester_servers() {
|
||||
PROCS=`ps -e | grep -E 'chia_harvester' | awk '!/grep/' | awk '{print $1}'`
|
||||
if [ -n "$PROCS" ];
|
||||
then
|
||||
echo "Shutting down harvesters"
|
||||
echo "$PROCS" | xargs -L1 kill
|
||||
echo "Restarting harvesters"
|
||||
_run_bg_cmd chia_harvester
|
||||
else
|
||||
echo "No running harvesters found"
|
||||
fi
|
||||
}
|
||||
|
||||
BG_PIDS=""
|
||||
_run_bg_cmd() {
|
||||
"$@" &
|
||||
BG_PIDS="$BG_PIDS $!"
|
||||
}
|
||||
|
||||
_restart_harvester_servers
|
||||
|
||||
_term() {
|
||||
echo "Caught TERM or INT signal, killing all servers."
|
||||
for PID in $BG_PIDS; do
|
||||
kill -TERM "$PID"
|
||||
done
|
||||
_kill_servers
|
||||
}
|
||||
|
||||
trap _term TERM
|
||||
trap _term INT
|
@ -1,3 +0,0 @@
|
||||
# Stops all python servers and VDF processes running on this machine
|
||||
|
||||
. _chia-common
|
16
setup.py
16
setup.py
@ -3,10 +3,10 @@ from setuptools import setup
|
||||
|
||||
dependencies = [
|
||||
"aiter==0.13.20191203", # Used for async generator tools
|
||||
"blspy==0.2.0", # Signature library
|
||||
"chiavdf==0.12.22", # timelord and vdf verification
|
||||
"blspy==0.2.1", # Signature library
|
||||
"chiavdf==0.12.23", # timelord and vdf verification
|
||||
"chiabip158==0.16", # bip158-style wallet filters
|
||||
"chiapos==0.12.23", # proof of space
|
||||
"chiapos==0.12.24", # proof of space
|
||||
"clvm==0.4", # contract language
|
||||
"clvm-tools==0.1.1", # clvm compiler tools
|
||||
"aiohttp==3.6.2", # HTTP server for full node rpc
|
||||
@ -69,18 +69,10 @@ kwargs = dict(
|
||||
"src.wallet.trading",
|
||||
"src.ssl",
|
||||
],
|
||||
scripts=[
|
||||
"scripts/_chia-common",
|
||||
"scripts/_chia-stop-wallet",
|
||||
"scripts/chia-drop-db",
|
||||
"scripts/chia-restart-harvester",
|
||||
"scripts/chia-start-sim",
|
||||
"scripts/chia-stop-all",
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"chia = src.cmds.chia:main",
|
||||
"chia-wallet = src.server.start_wallet:main",
|
||||
"chia_wallet = src.server.start_wallet:main",
|
||||
"chia_full_node = src.server.start_full_node:main",
|
||||
"chia_harvester = src.server.start_harvester:main",
|
||||
"chia_farmer = src.server.start_farmer:main",
|
||||
|
120
src/cmds/keys.py
120
src/cmds/keys.py
@ -1,4 +1,8 @@
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from blspy import AugSchemeMPL, G1Element, G2Element
|
||||
|
||||
from src.cmds.init import check_keys
|
||||
from src.util.keychain import (
|
||||
generate_mnemonic,
|
||||
@ -20,6 +24,8 @@ command_list = [
|
||||
"add",
|
||||
"delete",
|
||||
"delete_all",
|
||||
"sign",
|
||||
"verify",
|
||||
]
|
||||
|
||||
|
||||
@ -35,6 +41,12 @@ def help_message():
|
||||
"chia keys delete -f [fingerprint] (delete a key by it's pk fingerprint in hex form)"
|
||||
)
|
||||
print("chia keys delete_all (delete all private keys in keychain)")
|
||||
print(
|
||||
"chia keys sign -f [fingerprint] -t [hd_path] -d [message] (sign a message with a private key)"
|
||||
)
|
||||
print(
|
||||
"chia keys verify -p [public_key] -d [message] -s [signature] (verify a signature with a pk)"
|
||||
)
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
@ -54,7 +66,31 @@ def make_parser(parser):
|
||||
"--fingerprint",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Enter the fingerprint of the key you want to delete",
|
||||
help="Enter the fingerprint of the key you want to use",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--hd_path",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Enter the HD path in the form 'm/12381/8444/n/n'",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--message",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Enter the message to sign in UTF-8",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-p", "--public_key", type=str, default=None, help="Enter the pk in hex",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-s", "--signature", type=str, default=None, help="Enter the signature in hex",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@ -76,9 +112,8 @@ def generate_and_print():
|
||||
"""
|
||||
|
||||
mnemonic = generate_mnemonic()
|
||||
mnemonics_string = mnemonic_to_string(mnemonic)
|
||||
print("Generating private key. Mnemonic:")
|
||||
print(mnemonics_string)
|
||||
print("Generating private key. Mnemonic (24 secret words):")
|
||||
print(mnemonic)
|
||||
print(
|
||||
"Note that this key has not been added to the keychain. Run chia keys add_seed -m [MNEMONICS] to add"
|
||||
)
|
||||
@ -107,30 +142,13 @@ def add_private_key_seed(mnemonic):
|
||||
print(
|
||||
f"Added private key with public key fingerprint {fingerprint} and mnemonic"
|
||||
)
|
||||
print(f"{mnemonic_to_string(mnemonic)}")
|
||||
print(mnemonic)
|
||||
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
|
||||
def mnemonic_to_string(mnemonic_str):
|
||||
"""
|
||||
Converts a menmonic to a user readable string in the terminal.
|
||||
"""
|
||||
mnemonic = mnemonic_str.split()
|
||||
mnemonics_string = ""
|
||||
|
||||
for i in range(0, len(mnemonic)):
|
||||
mnemonics_string += f"{i + 1}) {mnemonic[i]}"
|
||||
if i != len(mnemonic) - 1:
|
||||
mnemonics_string += ", "
|
||||
if (i + 1) % 6 == 0:
|
||||
mnemonics_string += "\n"
|
||||
|
||||
return mnemonics_string
|
||||
|
||||
|
||||
def show_all_keys():
|
||||
"""
|
||||
Prints all keys and mnemonics (if available).
|
||||
@ -163,9 +181,8 @@ def show_all_keys():
|
||||
)
|
||||
assert seed is not None
|
||||
mnemonic = bytes_to_mnemonic(seed)
|
||||
mnemonic_string = mnemonic_to_string(mnemonic)
|
||||
print(" Mnemonic seed:")
|
||||
print(mnemonic_string)
|
||||
print(" Mnemonic seed (24 secret words):")
|
||||
print(mnemonic)
|
||||
|
||||
|
||||
def delete(args):
|
||||
@ -182,6 +199,55 @@ def delete(args):
|
||||
keychain.delete_key_by_fingerprint(fingerprint)
|
||||
|
||||
|
||||
def sign(args):
|
||||
if args.message is None:
|
||||
print("Please specify the message argument -d")
|
||||
quit()
|
||||
|
||||
if args.fingerprint is None or args.hd_path is None:
|
||||
print("Please specify the fingerprint argument -f and hd_path argument -t")
|
||||
quit()
|
||||
|
||||
message = args.message
|
||||
assert message is not None
|
||||
|
||||
k = Keychain()
|
||||
private_keys = k.get_all_private_keys()
|
||||
|
||||
fingerprint = args.fingerprint
|
||||
assert fingerprint is not None
|
||||
hd_path = args.hd_path
|
||||
assert hd_path is not None
|
||||
path: List[uint32] = [uint32(int(i)) for i in hd_path.split("/") if i != "m"]
|
||||
for sk, _ in private_keys:
|
||||
if sk.get_g1().get_fingerprint() == fingerprint:
|
||||
for c in path:
|
||||
sk = sk.derive_child(c)
|
||||
print("Public key:", sk.get_g1())
|
||||
print("Signature:", AugSchemeMPL.sign(sk, bytes(message, "utf-8")))
|
||||
return
|
||||
print(f"Fingerprint {fingerprint} not found in keychain")
|
||||
|
||||
|
||||
def verify(args):
|
||||
if args.message is None:
|
||||
print("Please specify the message argument -d")
|
||||
quit()
|
||||
if args.public_key is None:
|
||||
print("Please specify the public_key argument -p")
|
||||
quit()
|
||||
if args.signature is None:
|
||||
print("Please specify the signature argument -s")
|
||||
quit()
|
||||
assert args.message is not None
|
||||
assert args.public_key is not None
|
||||
assert args.signature is not None
|
||||
message = bytes(args.message, "utf-8")
|
||||
public_key = G1Element.from_bytes(bytes.fromhex(args.public_key))
|
||||
signature = G2Element.from_bytes(bytes.fromhex(args.signature))
|
||||
print(AugSchemeMPL.verify(public_key, message, signature))
|
||||
|
||||
|
||||
def handler(args, parser):
|
||||
if args.command is None or len(args.command) < 1:
|
||||
help_message()
|
||||
@ -213,3 +279,7 @@ def handler(args, parser):
|
||||
keychain.delete_all_keys()
|
||||
if command == "generate_and_print":
|
||||
generate_and_print()
|
||||
if command == "sign":
|
||||
sign(args)
|
||||
if command == "verify":
|
||||
verify(args)
|
||||
|
@ -101,7 +101,7 @@ async def netstorge_async(args, parser):
|
||||
network_space_terrabytes_estimate = network_space_bytes_estimate / 1024 ** 4
|
||||
print(
|
||||
f"The elapsed time between blocks is reported as {time_delta}.\n"
|
||||
f"The network has an estimated {network_space_terrabytes_estimate:.2f}TB"
|
||||
f"The network has an estimated {network_space_terrabytes_estimate:.2f}TiB"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
@ -1,6 +1,10 @@
|
||||
from pathlib import Path
|
||||
import logging
|
||||
from src.plotting.plot_tools import add_plot_directory
|
||||
from src.plotting.plot_tools import (
|
||||
add_plot_directory,
|
||||
remove_plot_directory,
|
||||
get_plot_directories,
|
||||
)
|
||||
from src.plotting.create_plots import create_plots
|
||||
from src.plotting.check_plots import check_plots
|
||||
from src.util.logging import initialize_logging
|
||||
@ -9,11 +13,7 @@ from src.util.logging import initialize_logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
command_list = [
|
||||
"create",
|
||||
"check",
|
||||
"add",
|
||||
]
|
||||
command_list = ["create", "check", "add", "remove", "show"]
|
||||
|
||||
|
||||
def help_message():
|
||||
@ -21,12 +21,14 @@ def help_message():
|
||||
print(f"command can be any of {command_list}")
|
||||
print("")
|
||||
print(
|
||||
"chia plots create -k [size] -n [number of plots] -b [memory buffer size MB]"
|
||||
"chia plots create -k [size] -n [number of plots] -b [memory buffer size MiB]"
|
||||
+ " -f [farmer pk] -p [pool pk] -t [tmp dir] -2 [tmp dir 2] -d [final dir] (creates plots)"
|
||||
)
|
||||
print("-s [sk_seed] -i [index] are available for debugging")
|
||||
print("chia plots check -n [num checks] (checks plots)")
|
||||
print("chia plots add -d [directory] (adds a directory of plots)")
|
||||
print("chia plots remove -d [directory] (removes a directory of plots from config)")
|
||||
print("chia plots show (shows the directory of current plots)")
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
@ -35,10 +37,7 @@ def make_parser(parser):
|
||||
"-n", "--num", help="Number of plots or challenges", type=int, default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i", "--index", help="First plot index", type=int, default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b", "--buffer", help="Megabytes for sort/plot buffer", type=int, default=2048
|
||||
"-b", "--buffer", help="Mebibytes for sort/plot buffer", type=int, default=2000
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
@ -50,9 +49,6 @@ def make_parser(parser):
|
||||
parser.add_argument(
|
||||
"-p", "--pool_public_key", help="Hex public key of pool", type=str, default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--sk_seed", help="Secret key seed in hex", type=str, default=None
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--tmp_dir",
|
||||
@ -85,6 +81,19 @@ def make_parser(parser):
|
||||
parser.print_help = lambda self=parser: help_message()
|
||||
|
||||
|
||||
def show(root_path):
|
||||
print("Directories where plots are being searched for:")
|
||||
print("Note that subdirectories must be added manually.")
|
||||
print(
|
||||
"Add with 'chia plots add -d [dir]' and remove with"
|
||||
+ " 'chia plots remove -d [dir]'."
|
||||
+ " Scan and check plots with 'chia plots check'"
|
||||
)
|
||||
print()
|
||||
for str_path in get_plot_directories(root_path):
|
||||
print(f"{str_path}")
|
||||
|
||||
|
||||
def handler(args, parser):
|
||||
if args.command is None or len(args.command) < 1:
|
||||
help_message()
|
||||
@ -109,3 +118,8 @@ def handler(args, parser):
|
||||
elif command == "add":
|
||||
str_path = args.final_dir
|
||||
add_plot_directory(str_path, root_path)
|
||||
elif command == "remove":
|
||||
str_path = args.final_dir
|
||||
remove_plot_directory(str_path, root_path)
|
||||
elif command == "show":
|
||||
show(root_path)
|
||||
|
@ -3,15 +3,17 @@ import asyncio
|
||||
import time
|
||||
from time import struct_time, localtime
|
||||
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
from src.server.connection import NodeType
|
||||
from src.types.header_block import HeaderBlock
|
||||
from src.rpc.full_node_rpc_client import FullNodeRpcClient
|
||||
from src.rpc.wallet_rpc_client import WalletRpcClient
|
||||
from src.util.byte_types import hexstr_to_bytes
|
||||
from src.util.config import str2bool
|
||||
from src.util.config import load_config
|
||||
from src.util.default_root import DEFAULT_ROOT_PATH
|
||||
from src.cmds.units import units
|
||||
|
||||
|
||||
def make_parser(parser):
|
||||
@ -86,6 +88,26 @@ def make_parser(parser):
|
||||
type=int,
|
||||
default=8555,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-wp",
|
||||
"--wallet-rpc-port",
|
||||
help="Set the port where the Wallet is hosting the RPC interface."
|
||||
+ " See the rpc_port under wallet in config.yaml."
|
||||
+ "Defaults to 9256",
|
||||
type=int,
|
||||
default=9256,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
"--wallet-balances",
|
||||
help="Display wallet balances.",
|
||||
type=str2bool,
|
||||
nargs="?",
|
||||
const=True,
|
||||
default=False,
|
||||
)
|
||||
parser.set_defaults(function=show)
|
||||
|
||||
|
||||
@ -178,7 +200,7 @@ async def show_async(args, parser):
|
||||
print("Connections")
|
||||
print(
|
||||
"Type IP Ports NodeID Last Connect"
|
||||
+ " MB Up|Dwn"
|
||||
+ " MiB Up|Dwn"
|
||||
)
|
||||
for con in connections:
|
||||
last_connect_tuple = struct_time(localtime(con["last_message_time"]))
|
||||
@ -262,7 +284,7 @@ async def show_async(args, parser):
|
||||
if block.header.data.aggregated_signature is None:
|
||||
aggregated_signature = block.header.data.aggregated_signature
|
||||
else:
|
||||
aggregated_signature = block.header.data.aggregated_signature.sig
|
||||
aggregated_signature = block.header.data.aggregated_signature
|
||||
print("Block", block.header.data.height, ":")
|
||||
print(
|
||||
f"Header Hash 0x{args.block_by_header_hash}\n"
|
||||
@ -289,6 +311,55 @@ async def show_async(args, parser):
|
||||
else:
|
||||
print("Block with header hash", args.block_by_header_hash, "not found.")
|
||||
|
||||
if args.wallet_balances:
|
||||
if "wallet_rpc_port" not in args or args.wallet_rpc_port is None:
|
||||
wallet_rpc_port = config["wallet"]["rpc_port"]
|
||||
else:
|
||||
wallet_rpc_port = args.wallet_rpc_port
|
||||
wallet_client = await WalletRpcClient.create(self_hostname, wallet_rpc_port)
|
||||
summaries: Dict = await wallet_client.get_wallet_summaries()
|
||||
print("Balances")
|
||||
for wallet_id, summary in summaries.items():
|
||||
balances = await wallet_client.get_wallet_balance(wallet_id)
|
||||
if "name" in summary:
|
||||
print(
|
||||
f"Wallet ID {wallet_id} type {summary['type']} {summary['name']}"
|
||||
)
|
||||
print(
|
||||
f" -Confirmed: {balances['confirmed_wallet_balance']/units['colouredcoin']}"
|
||||
)
|
||||
print(
|
||||
f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}"
|
||||
)
|
||||
print(
|
||||
f" -Spendable: {balances['spendable_balance']/units['colouredcoin']}"
|
||||
)
|
||||
print(
|
||||
f" -Frozen: {balances['frozen_balance']/units['colouredcoin']}"
|
||||
)
|
||||
print(
|
||||
f" -Pending change: {balances['pending_change']/units['colouredcoin']}"
|
||||
)
|
||||
else:
|
||||
print(f"Wallet ID {wallet_id} type {summary['type']}")
|
||||
print(
|
||||
f" -Confirmed: {balances['confirmed_wallet_balance']/units['chia']} TXCH"
|
||||
)
|
||||
print(
|
||||
f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['chia']} TXCH"
|
||||
)
|
||||
print(
|
||||
f" -Spendable: {balances['spendable_balance']/units['chia']} TXCH"
|
||||
)
|
||||
print(
|
||||
f" -Frozen: {balances['frozen_balance']/units['chia']} TXCH"
|
||||
)
|
||||
print(
|
||||
f" -Pending change: {balances['pending_change']/units['chia']} TXCH"
|
||||
)
|
||||
wallet_client.close()
|
||||
await wallet_client.await_closed()
|
||||
|
||||
except Exception as e:
|
||||
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
|
||||
print(f"Connection error. Check if full node is running at {args.rpc_port}")
|
||||
|
9
src/cmds/units.py
Normal file
9
src/cmds/units.py
Normal file
@ -0,0 +1,9 @@
|
||||
from typing import Dict
|
||||
|
||||
# The rest of the codebase uses mojos everywhere. Only uses these units
|
||||
# for user facing interfaces
|
||||
units: Dict[str, int] = {
|
||||
"chia": 10 ** 12, # 1 chia (XCH) is 1000000000000 mojo
|
||||
"mojo:": 1,
|
||||
"colouredcoin": 10 ** 3, # 1 coloured coin is 1000 colouredcoin mojos
|
||||
}
|
@ -99,7 +99,7 @@ testnet_kwargs = {
|
||||
# Coinbase rewards are not spendable for 200 blocks
|
||||
"COINBASE_FREEZE_PERIOD": 200,
|
||||
# Max coin amount uint(1 << 64)
|
||||
"MAX_COIN_AMOUNT": 0xffffffffffffffff,
|
||||
"MAX_COIN_AMOUNT": 0xFFFFFFFFFFFFFFFF,
|
||||
# Raw size per block target = 1,000,000 bytes
|
||||
# Rax TX (single in, single out) = 219 bytes (not compressed)
|
||||
# TX = 457 vBytes
|
||||
|
@ -35,7 +35,7 @@ log = logging.getLogger(__name__)
|
||||
if getattr(sys, "frozen", False):
|
||||
name_map = {
|
||||
"chia": "chia",
|
||||
"chia-wallet": "wallet_server",
|
||||
"chia_wallet": "start_wallet",
|
||||
"chia_full_node": "start_full_node",
|
||||
"chia_harvester": "start_harvester",
|
||||
"chia_farmer": "start_farmer",
|
||||
@ -212,6 +212,7 @@ class WebSocketServer:
|
||||
t = request["t"]
|
||||
t2 = request["t2"]
|
||||
d = request["d"]
|
||||
b = request["b"]
|
||||
|
||||
command_args: List[str] = []
|
||||
command_args += service_name.split(" ")
|
||||
@ -220,6 +221,7 @@ class WebSocketServer:
|
||||
command_args.append(f"-t={t}")
|
||||
command_args.append(f"-2={t2}")
|
||||
command_args.append(f"-d={d}")
|
||||
command_args.append(f"-b={b}")
|
||||
|
||||
error = None
|
||||
success = False
|
||||
|
@ -221,7 +221,9 @@ class Blockchain:
|
||||
prev_challenge_hash = block.proof_of_space.challenge_hash
|
||||
|
||||
new_difficulty: Optional[uint64]
|
||||
if (block.height + 1) % self.constants.DIFFICULTY_EPOCH == self.constants.DIFFICULTY_DELAY:
|
||||
if (
|
||||
block.height + 1
|
||||
) % self.constants.DIFFICULTY_EPOCH == self.constants.DIFFICULTY_DELAY:
|
||||
new_difficulty = get_next_difficulty(
|
||||
self.constants, self.headers, self.height_to_hash, block.header
|
||||
)
|
||||
@ -769,6 +771,7 @@ class Blockchain:
|
||||
# This coin is not in the current heaviest chain, so it must be in the fork
|
||||
if rem not in additions_since_fork:
|
||||
# This coin does not exist in the fork
|
||||
# TODO: fix this, there is a consensus bug here
|
||||
return Err.UNKNOWN_UNSPENT
|
||||
if rem in coinbases_since_fork:
|
||||
# This coin is a coinbase coin
|
||||
|
@ -128,9 +128,7 @@ def get_next_difficulty(
|
||||
)
|
||||
# Take only DIFFICULTY_SIGNIFICANT_BITS significant bits
|
||||
new_difficulty = uint64(
|
||||
truncate_to_significant_bits(
|
||||
new_difficulty_precise, constants.SIGNIFICANT_BITS
|
||||
)
|
||||
truncate_to_significant_bits(new_difficulty_precise, constants.SIGNIFICANT_BITS)
|
||||
)
|
||||
assert count_significant_bits(new_difficulty) <= constants.SIGNIFICANT_BITS
|
||||
|
||||
|
@ -1722,7 +1722,11 @@ class FullNode:
|
||||
) -> OutboundMessageGenerator:
|
||||
if self.global_connections is None:
|
||||
return
|
||||
peers = self.global_connections.peers.get_peers(recent_threshold=24 * 60 * 60)
|
||||
connected_peers = self.global_connections.get_full_node_peerinfos()
|
||||
unconnected_peers = self.global_connections.peers.get_peers(
|
||||
recent_threshold=24 * 60 * 60
|
||||
)
|
||||
peers = list(set(connected_peers + unconnected_peers))
|
||||
|
||||
yield OutboundMessage(
|
||||
NodeType.FULL_NODE,
|
||||
|
@ -13,10 +13,15 @@ from src.protocols import harvester_protocol
|
||||
from src.server.connection import PeerConnections
|
||||
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
|
||||
from src.types.proof_of_space import ProofOfSpace
|
||||
from src.util.config import load_config, save_config
|
||||
from src.util.api_decorators import api_request
|
||||
from src.util.ints import uint8
|
||||
from src.plotting.plot_tools import load_plots, PlotInfo
|
||||
from src.plotting.plot_tools import (
|
||||
load_plots,
|
||||
PlotInfo,
|
||||
remove_plot_directory,
|
||||
add_plot_directory,
|
||||
get_plot_directories,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -24,7 +29,7 @@ log = logging.getLogger(__name__)
|
||||
class Harvester:
|
||||
config: Dict
|
||||
provers: Dict[Path, PlotInfo]
|
||||
failed_to_open_filenames: Set[Path]
|
||||
failed_to_open_filenames: Dict[Path, int]
|
||||
no_key_filenames: Set[Path]
|
||||
farmer_public_keys: List[G1Element]
|
||||
pool_public_keys: List[G1Element]
|
||||
@ -41,7 +46,7 @@ class Harvester:
|
||||
|
||||
# From filename to prover
|
||||
self.provers = {}
|
||||
self.failed_to_open_filenames = set()
|
||||
self.failed_to_open_filenames = {}
|
||||
self.no_key_filenames = set()
|
||||
|
||||
self._is_shutdown = False
|
||||
@ -93,24 +98,28 @@ class Harvester:
|
||||
|
||||
return (
|
||||
response_plots,
|
||||
[str(s) for s in self.failed_to_open_filenames],
|
||||
[str(s) for s, _ in self.failed_to_open_filenames.items()],
|
||||
[str(s) for s in self.no_key_filenames],
|
||||
)
|
||||
|
||||
async def _refresh_plots(self):
|
||||
locked: bool = self._refresh_lock.locked()
|
||||
changed: bool = False
|
||||
async with self._refresh_lock:
|
||||
(
|
||||
changed,
|
||||
self.provers,
|
||||
self.failed_to_open_filenames,
|
||||
self.no_key_filenames,
|
||||
) = load_plots(
|
||||
self.provers,
|
||||
self.failed_to_open_filenames,
|
||||
self.farmer_public_keys,
|
||||
self.pool_public_keys,
|
||||
self.root_path,
|
||||
)
|
||||
if not locked:
|
||||
# Avoid double refreshing of plots
|
||||
(
|
||||
changed,
|
||||
self.provers,
|
||||
self.failed_to_open_filenames,
|
||||
self.no_key_filenames,
|
||||
) = load_plots(
|
||||
self.provers,
|
||||
self.failed_to_open_filenames,
|
||||
self.farmer_public_keys,
|
||||
self.pool_public_keys,
|
||||
self.root_path,
|
||||
)
|
||||
if changed:
|
||||
self._state_changed("plots")
|
||||
|
||||
@ -127,13 +136,15 @@ class Harvester:
|
||||
return True
|
||||
|
||||
async def _add_plot_directory(self, str_path: str) -> bool:
|
||||
config = load_config(self.root_path, "config.yaml")
|
||||
if str(Path(str_path).resolve()) not in config["harvester"]["plot_directories"]:
|
||||
config["harvester"]["plot_directories"].append(
|
||||
str(Path(str_path).resolve())
|
||||
)
|
||||
save_config(self.root_path, "config.yaml", config)
|
||||
await self._refresh_plots()
|
||||
add_plot_directory(str_path, self.root_path)
|
||||
await self._refresh_plots()
|
||||
return True
|
||||
|
||||
async def _get_plot_directories(self) -> List[str]:
|
||||
return get_plot_directories(self.root_path)
|
||||
|
||||
async def _remove_plot_directory(self, str_path: str) -> bool:
|
||||
remove_plot_directory(str_path, self.root_path)
|
||||
return True
|
||||
|
||||
def _set_global_connections(self, global_connections: Optional[PeerConnections]):
|
||||
@ -195,14 +206,14 @@ class Harvester:
|
||||
quality_strings = prover.get_qualities_for_challenge(
|
||||
new_challenge.challenge_hash
|
||||
)
|
||||
except RuntimeError:
|
||||
except Exception:
|
||||
log.error("Error using prover object. Reinitializing prover object.")
|
||||
try:
|
||||
self.prover = DiskProver(str(filename))
|
||||
quality_strings = self.prover.get_qualities_for_challenge(
|
||||
new_challenge.challenge_hash
|
||||
)
|
||||
except RuntimeError:
|
||||
except Exception:
|
||||
log.error(
|
||||
f"Retry-Error using prover object on {filename}. Giving up."
|
||||
)
|
||||
@ -231,7 +242,7 @@ class Harvester:
|
||||
|
||||
awaitables = []
|
||||
for filename, plot_info in self.provers.items():
|
||||
if ProofOfSpace.can_create_proof(
|
||||
if filename.exists() and ProofOfSpace.can_create_proof(
|
||||
plot_info.prover.get_id(),
|
||||
new_challenge.challenge_hash,
|
||||
self.constants.NUMBER_ZERO_BITS_CHALLENGE_SIG,
|
||||
|
@ -28,7 +28,7 @@ def check_plots(args, root_path):
|
||||
for pk in config["farmer"]["pool_public_keys"]
|
||||
]
|
||||
_, provers, failed_to_open_filenames, no_key_filenames = load_plots(
|
||||
{}, set(), pks, pool_public_keys, root_path, open_no_key_filenames=True,
|
||||
{}, {}, pks, pool_public_keys, root_path, open_no_key_filenames=True,
|
||||
)
|
||||
if len(provers) > 0:
|
||||
log.info("")
|
||||
@ -78,7 +78,7 @@ def check_plots(args, root_path):
|
||||
log.info("Summary")
|
||||
total_plots: int = sum(list(total_good_plots.values()))
|
||||
log.info(
|
||||
f"Found {total_plots} valid plots, total size {total_size / (1024 * 1024 * 1024 * 1024):.5f} TB"
|
||||
f"Found {total_plots} valid plots, total size {total_size / (1024 * 1024 * 1024 * 1024):.5f} TiB"
|
||||
)
|
||||
for (k, count) in sorted(dict(total_good_plots).items()):
|
||||
log.info(f"{count} plots of size {k}")
|
||||
|
@ -3,6 +3,7 @@ from pathlib import Path
|
||||
from blspy import PrivateKey, G1Element
|
||||
from chiapos import DiskProver
|
||||
from dataclasses import dataclass
|
||||
import time
|
||||
import logging
|
||||
import traceback
|
||||
from src.types.proof_of_space import ProofOfSpace
|
||||
@ -70,7 +71,7 @@ def stream_plot_info(
|
||||
return data
|
||||
|
||||
|
||||
def add_plot_directory(str_path, root_path):
|
||||
def add_plot_directory(str_path: str, root_path: Path) -> Dict:
|
||||
config = load_config(root_path, "config.yaml")
|
||||
if str(Path(str_path).resolve()) not in config["harvester"]["plot_directories"]:
|
||||
config["harvester"]["plot_directories"].append(str(Path(str_path).resolve()))
|
||||
@ -78,14 +79,38 @@ def add_plot_directory(str_path, root_path):
|
||||
return config
|
||||
|
||||
|
||||
def get_plot_directories(root_path: Path) -> List[str]:
|
||||
config = load_config(root_path, "config.yaml")
|
||||
return [
|
||||
str(Path(str_path).resolve())
|
||||
for str_path in config["harvester"]["plot_directories"]
|
||||
]
|
||||
|
||||
|
||||
def remove_plot_directory(str_path: str, root_path: Path) -> None:
|
||||
config = load_config(root_path, "config.yaml")
|
||||
str_paths: List[str] = config["harvester"]["plot_directories"]
|
||||
# If path str matches exactly, remove
|
||||
if str_path in str_paths:
|
||||
str_paths.remove(str_path)
|
||||
|
||||
# If path matcehs full path, remove
|
||||
new_paths = [Path(sp).resolve() for sp in str_paths]
|
||||
if Path(str_path).resolve() in new_paths:
|
||||
new_paths.remove(Path(str_path).resolve())
|
||||
|
||||
config["harvester"]["plot_directories"] = [str(np) for np in new_paths]
|
||||
save_config(root_path, "config.yaml", config)
|
||||
|
||||
|
||||
def load_plots(
|
||||
provers: Dict[Path, PlotInfo],
|
||||
failed_to_open_filenames: Set[Path],
|
||||
failed_to_open_filenames: Dict[Path, int],
|
||||
farmer_public_keys: Optional[List[G1Element]],
|
||||
pool_public_keys: Optional[List[G1Element]],
|
||||
root_path: Path,
|
||||
open_no_key_filenames=False,
|
||||
) -> Tuple[bool, Dict[Path, PlotInfo], Set[Path], Set[Path]]:
|
||||
) -> Tuple[bool, Dict[Path, PlotInfo], Dict[Path, int], Set[Path]]:
|
||||
config_file = load_config(root_path, "config.yaml", "harvester")
|
||||
changed = False
|
||||
no_key_filenames: Set[Path] = set()
|
||||
@ -96,16 +121,22 @@ def load_plots(
|
||||
for paths in plot_filenames.values():
|
||||
all_filenames += paths
|
||||
total_size = 0
|
||||
new_provers: Dict[Path, PlotInfo] = {}
|
||||
|
||||
for filename in all_filenames:
|
||||
if filename in provers:
|
||||
stat_info = filename.stat()
|
||||
if stat_info.st_mtime == provers[filename].time_modified:
|
||||
total_size += stat_info.st_size
|
||||
continue
|
||||
if filename in failed_to_open_filenames:
|
||||
continue
|
||||
if filename.exists():
|
||||
if (
|
||||
filename in failed_to_open_filenames
|
||||
and (time.time() - failed_to_open_filenames[filename]) < 1200
|
||||
):
|
||||
# Try once every 20 minutes to open the file
|
||||
continue
|
||||
if filename in provers:
|
||||
stat_info = filename.stat()
|
||||
if stat_info.st_mtime == provers[filename].time_modified:
|
||||
total_size += stat_info.st_size
|
||||
new_provers[filename] = provers[filename]
|
||||
continue
|
||||
try:
|
||||
prover = DiskProver(str(filename))
|
||||
(
|
||||
@ -141,7 +172,7 @@ def load_plots(
|
||||
plot_public_key: G1Element = ProofOfSpace.generate_plot_public_key(
|
||||
local_sk.get_g1(), farmer_public_key
|
||||
)
|
||||
provers[filename] = PlotInfo(
|
||||
new_provers[filename] = PlotInfo(
|
||||
prover,
|
||||
pool_public_key,
|
||||
farmer_public_key,
|
||||
@ -155,13 +186,13 @@ def load_plots(
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
log.error(f"Failed to open file {filename}. {e} {tb}")
|
||||
failed_to_open_filenames.add(filename)
|
||||
failed_to_open_filenames[filename] = int(time.time())
|
||||
continue
|
||||
log.info(
|
||||
f"Found plot {filename} of size {provers[filename].prover.get_size()}"
|
||||
f"Found plot {filename} of size {new_provers[filename].prover.get_size()}"
|
||||
)
|
||||
|
||||
log.info(
|
||||
f"Loaded a total of {len(provers)} plots of size {total_size / (1024 ** 4)} TB"
|
||||
f"Loaded a total of {len(new_provers)} plots of size {total_size / (1024 ** 4)} TiB"
|
||||
)
|
||||
return (changed, provers, failed_to_open_filenames, no_key_filenames)
|
||||
return (changed, new_provers, failed_to_open_filenames, no_key_filenames)
|
||||
|
@ -15,6 +15,8 @@ class HarvesterRpcApi:
|
||||
"/refresh_plots": self.refresh_plots,
|
||||
"/delete_plot": self.delete_plot,
|
||||
"/add_plot_directory": self.add_plot_directory,
|
||||
"/get_plot_directories": self.get_plot_directories,
|
||||
"/remove_plot_directory": self.remove_plot_directory,
|
||||
}
|
||||
|
||||
async def _state_changed(self, change: str) -> List[str]:
|
||||
@ -46,3 +48,12 @@ class HarvesterRpcApi:
|
||||
dirname = request["dirname"]
|
||||
success = await self.service._add_plot_directory(dirname)
|
||||
return {"success": success}
|
||||
|
||||
async def get_plot_directories(self, request: Dict) -> Dict:
|
||||
plot_dirs = await self.service._get_plot_directories()
|
||||
return {"success": True, "directories": plot_dirs}
|
||||
|
||||
async def remove_plot_directory(self, request: Dict) -> Dict:
|
||||
dirname = request["dirname"]
|
||||
success = await self.service._remove_plot_directory(dirname)
|
||||
return {"success": success}
|
||||
|
@ -21,4 +21,12 @@ class HarvesterRpcClient(RpcClient):
|
||||
return await self.fetch("delete_plot", {"filename": filename})
|
||||
|
||||
async def add_plot_directory(self, dirname: str) -> bool:
|
||||
return await self.fetch("add_plot_directory", {"dirname": dirname})
|
||||
return (await self.fetch("add_plot_directory", {"dirname": dirname}))["success"]
|
||||
|
||||
async def get_plot_directories(self) -> List[str]:
|
||||
return (await self.fetch("get_plot_directories", {}))["directories"]
|
||||
|
||||
async def remove_plot_directory(self, dirname: str) -> bool:
|
||||
return (await self.fetch("remove_plot_directory", {"dirname": dirname}))[
|
||||
"success"
|
||||
]
|
||||
|
@ -155,7 +155,7 @@ class RpcServer:
|
||||
self.log.info(f"Rpc call <- {message['command']}")
|
||||
response = await self.ws_api(message)
|
||||
if response is not None:
|
||||
log.info(f"RPC response -> {message['command']}")
|
||||
log.info(f"Rpc response -> {message['command']}")
|
||||
await websocket.send_str(format_response(message, response))
|
||||
|
||||
except Exception as e:
|
||||
|
@ -35,7 +35,7 @@ log = logging.getLogger(__name__)
|
||||
class WalletRpcApi:
|
||||
def __init__(self, wallet_node: WalletNode):
|
||||
self.service = wallet_node
|
||||
self.service_name = "chia-wallet"
|
||||
self.service_name = "chia_wallet"
|
||||
|
||||
def get_routes(self) -> Dict[str, Callable]:
|
||||
return {
|
||||
@ -137,7 +137,7 @@ class WalletRpcApi:
|
||||
}
|
||||
if wallet_id is not None:
|
||||
data["wallet_id"] = wallet_id
|
||||
return [create_payload("state_changed", data, "chia-wallet", "wallet_ui")]
|
||||
return [create_payload("state_changed", data, "chia_wallet", "wallet_ui")]
|
||||
|
||||
async def get_next_puzzle_hash(self, request: Dict) -> Dict:
|
||||
"""
|
||||
@ -574,7 +574,22 @@ class WalletRpcApi:
|
||||
# Adding a key from 24 word mnemonic
|
||||
mnemonic = request["mnemonic"]
|
||||
passphrase = ""
|
||||
sk = self.service.keychain.add_private_key(" ".join(mnemonic), passphrase)
|
||||
try:
|
||||
sk = self.service.keychain.add_private_key(
|
||||
" ".join(mnemonic), passphrase
|
||||
)
|
||||
except KeyError as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"The word '{e.args[0]}' is incorrect.'",
|
||||
"word": e.args[0],
|
||||
}
|
||||
except ValueError as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": e.args[0],
|
||||
}
|
||||
|
||||
else:
|
||||
return {"success": False}
|
||||
|
||||
@ -609,7 +624,8 @@ class WalletRpcApi:
|
||||
fingerprint = request["fingerprint"]
|
||||
self.service.keychain.delete_key_by_fingerprint(fingerprint)
|
||||
path = path_from_root(
|
||||
self.service.root_path, f"{self.service.config['database_path']}-{fingerprint}"
|
||||
self.service.root_path,
|
||||
f"{self.service.config['database_path']}-{fingerprint}",
|
||||
)
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
|
18
src/rpc/wallet_rpc_client.py
Normal file
18
src/rpc/wallet_rpc_client.py
Normal file
@ -0,0 +1,18 @@
|
||||
from typing import Dict
|
||||
from src.rpc.rpc_client import RpcClient
|
||||
|
||||
|
||||
class WalletRpcClient(RpcClient):
|
||||
"""
|
||||
Client to Chia RPC, connects to a local wallet. Uses HTTP/JSON, and converts back from
|
||||
JSON into native python objects before returning. All api calls use POST requests.
|
||||
Note that this is not the same as the peer protocol, or wallet protocol (which run Chia's
|
||||
protocol on top of TCP), it's a separate protocol on top of HTTP thats provides easy access
|
||||
to the full node.
|
||||
"""
|
||||
|
||||
async def get_wallet_summaries(self) -> Dict:
|
||||
return await self.fetch("get_wallet_summaries", {})
|
||||
|
||||
async def get_wallet_balance(self, wallet_id: str) -> Dict:
|
||||
return await self.fetch("get_wallet_balance", {"wallet_id": wallet_id})
|
@ -23,6 +23,9 @@ from src.util.ints import uint16
|
||||
import traceback
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def initialize_pipeline(
|
||||
srwt_aiter,
|
||||
api: Any,
|
||||
@ -161,8 +164,11 @@ async def connection_to_outbound(
|
||||
"""
|
||||
connection, global_connections = pair
|
||||
if connection.on_connect:
|
||||
async for outbound_message in connection.on_connect():
|
||||
yield connection, outbound_message, global_connections
|
||||
try:
|
||||
async for outbound_message in connection.on_connect():
|
||||
yield connection, outbound_message, global_connections
|
||||
except Exception as e:
|
||||
connection.log.warning(f"Exception in on_connect: {e}")
|
||||
|
||||
|
||||
async def perform_handshake(
|
||||
@ -228,7 +234,7 @@ async def perform_handshake(
|
||||
)
|
||||
# Only yield a connection if the handshake is succesful and the connection is not a duplicate.
|
||||
yield connection, global_connections
|
||||
except (ProtocolError, asyncio.IncompleteReadError, OSError, Exception,) as e:
|
||||
except Exception as e:
|
||||
connection.log.warning(f"{e}, handshake not completed. Connection not created.")
|
||||
# Make sure to close the connection even if it's not in global connections
|
||||
connection.close()
|
||||
|
@ -79,6 +79,9 @@ class ChiaServer:
|
||||
# Aiter used to broadcase messages
|
||||
self._outbound_aiter: push_aiter = push_aiter()
|
||||
|
||||
# Open connection tasks. These will be cancelled if
|
||||
self._oc_tasks: List[asyncio.Task] = []
|
||||
|
||||
# Taks list to keep references to tasks, so they don'y get GCd
|
||||
self._tasks: List[asyncio.Task] = []
|
||||
if local_type != NodeType.INTRODUCER:
|
||||
@ -130,9 +133,16 @@ class ChiaServer:
|
||||
|
||||
ssl_context = ssl_context_for_client(self.root_path, self.config, auth=auth)
|
||||
try:
|
||||
reader, writer = await asyncio.open_connection(
|
||||
target_node.host, int(target_node.port), ssl=ssl_context
|
||||
# Sometimes open_connection takes a long time, so we add it as a task, and cancel
|
||||
# the task in the event of closing the node.
|
||||
oc_task: asyncio.Task = asyncio.create_task(
|
||||
asyncio.open_connection(
|
||||
target_node.host, int(target_node.port), ssl=ssl_context
|
||||
)
|
||||
)
|
||||
self._oc_tasks.append(oc_task)
|
||||
reader, writer = await oc_task
|
||||
self._oc_tasks.remove(oc_task)
|
||||
except Exception as e:
|
||||
self.log.warning(
|
||||
f"Could not connect to {target_node}. {type(e)}{str(e)}. Aborting and removing peer."
|
||||
@ -170,6 +180,8 @@ class ChiaServer:
|
||||
self._outbound_aiter.stop()
|
||||
if not self._srwt_aiter.is_stopped():
|
||||
self._srwt_aiter.stop()
|
||||
for task in self._oc_tasks:
|
||||
task.cancel()
|
||||
|
||||
def _initialize_ping_task(self):
|
||||
async def ping():
|
||||
|
@ -90,6 +90,7 @@ class Service:
|
||||
assert network_id is not None
|
||||
|
||||
self._node_type = node_type
|
||||
self._service_name = service_name
|
||||
|
||||
proctitle_name = f"chia_{service_name}"
|
||||
setproctitle(proctitle_name)
|
||||
@ -186,41 +187,51 @@ class Service:
|
||||
except NotImplementedError:
|
||||
self._log.info("signal handlers unsupported")
|
||||
|
||||
for _ in self._server_sockets:
|
||||
await _.wait_closed()
|
||||
|
||||
await self._server.await_closed()
|
||||
if self._stop_callback:
|
||||
self._stop_callback()
|
||||
if self._await_closed_callback:
|
||||
await self._await_closed_callback()
|
||||
|
||||
self._task = asyncio.create_task(_run())
|
||||
|
||||
async def run(self):
|
||||
self.start()
|
||||
await self.wait_closed()
|
||||
self._log.info("Closed all node servers.")
|
||||
return 0
|
||||
|
||||
def stop(self):
|
||||
if not self._is_stopping:
|
||||
self._is_stopping = True
|
||||
self._log.info("Calling service stop callback")
|
||||
if self._stop_callback:
|
||||
self._stop_callback()
|
||||
self._log.info("Closing server sockets")
|
||||
for _ in self._server_sockets:
|
||||
_.close()
|
||||
self._log.info("Cancelling reconnect task")
|
||||
for _ in self._reconnect_tasks:
|
||||
_.cancel()
|
||||
self._log.info("Closing connections")
|
||||
self._server.close_all()
|
||||
self._api._shut_down = True
|
||||
self._log.info("Stopping introducer task")
|
||||
if self._introducer_poll_task:
|
||||
self._introducer_poll_task.cancel()
|
||||
|
||||
async def wait_closed(self):
|
||||
await self._task
|
||||
self._log.info("Waiting for socket to be closed (if opened)")
|
||||
for _ in self._server_sockets:
|
||||
await _.wait_closed()
|
||||
|
||||
self._log.info("Waiting for ChiaServer to be closed")
|
||||
await self._server.await_closed()
|
||||
if self._rpc_task:
|
||||
|
||||
self._log.info("Waiting for RPC server")
|
||||
await (await self._rpc_task)()
|
||||
self._log.info("Closed RPC server.")
|
||||
self._log.info(f"Service at port {self._advertised_port} fully closed")
|
||||
|
||||
if self._await_closed_callback:
|
||||
self._log.info("Waiting for service _await_closed callback")
|
||||
await self._await_closed_callback()
|
||||
self._log.info(
|
||||
f"Service {self._service_name} at port {self._advertised_port} fully closed"
|
||||
)
|
||||
|
||||
|
||||
async def async_run_service(*args, **kwargs):
|
||||
|
@ -15,7 +15,7 @@ test_constants = make_test_constants_without_genesis(
|
||||
"NUMBER_ZERO_BITS_CHALLENGE_SIG": 1,
|
||||
"CLVM_COST_RATIO_CONSTANT": 108,
|
||||
"COINBASE_FREEZE_PERIOD": 0,
|
||||
"GENESIS_BLOCK": b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x883|\xde\xeb0\x9e[1:\x97\xe1Jp\x1e\x85|r9 Q\x81*\x058\xe9\xef\xeb\x89\x0c\x8b\xaft\xe6T\xfd\xf2W[\x01\xf0\x16\xc4\n`\xeb\xbd%\x84m\x82\xb5\xc6\xaa\xe8p\x8e\x8f\xa1\x814Rw\xa2\x9b\xce\xd2\x8a\x94\xfc\xb8\x90o\\\xad:;\x12\x9f\xac\xe2f\xe0\x13\x00\xe9]\x83\xa3\xb6\x8f\x15\x8ds\x91\xdd\x12\x00\x00\x00\x90\x9b2K\xbd\x14\xbc\x85/\x06\xad\xc4\xa0%H\xd5\x81\x0b\xe8\x0e\x0c+\xa3W^(\x84e\xb5\xa9I*\xc7L\xb2\x83L\xc4\xca\xe2\xa9\x9a\xc6\xba\xc7\x9b"s\xd0O\xf9\x06&,4\x82tX-I[\xaf\t6Na\x01\x19!;\xbb\x13\x0b\x9bT\x19\x8c\xf6Q2q\x19\taYhL\xc84(\xaa6\xfb\x9aA\xfc\x06\xc7#\xab\xe0\x1c\x843\xf3\xf7k\xd0\xa9\x087N\x1d\x92\x97<o3+\x10J\xf5\xad\xdc\xfb\xa5\x8b[t\xea\xcd\x00uB\xcf\x1e\xf8\xb7\x15\xd6=\x8b;\xc0\xf0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x05\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\r\xc0\x88F\r6\x84\x9d\x85\xc1\xa2\x9aq\xf7\x1cFXm\xf5L\x9f \xbb\x1d\x10\x97\x84\xb5$\x01\xc9^\x7f2\xfb\xee\xeb\x85\xa6\x02\xe3l\xba\xee+\xa25\x00\xd4\xe1\x90/\x08\x97\xef\x18#\xb7\xe5+\xb2\xb9\xc8\xb9\xce\xe1\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06\xf8\xa8}\xd4_\x98QR\x14"\x06<\xa4;\x9f2\xd3\xed\xd0mK\xf7\xfa~<\x88B\x8b\xd4F\xdcHp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\xc1\xef\x962\x87V\xc4\xd5\xd5AL\x12\x1c\xe4|{\xc0\x7f{.\xe98\x93%\x08:\xa66\xe7\x80Q\x00\x00\x01\xd1\xa9J \x00+[\xbbW\xc5\xdaVh\x93\x16Q\xaa\r\x12\t"\xe3?:fk\xfd\x1f\x82q\xd0\r\x02U\xcc\xf9\xdb\x00\x00\x00\x00\xa3\xaa\xa0\xe0\xediiw+\xae\xecY\r{4\x9f\xc5\x82\'"\x9d\x8dI\x03Y{\x82\xd7pY\xb5\xd7\xc8\x0c-9\xff\xb141\xa5\xee\x17\x01\x1b\x0b&t\xa9\xf6\xe9\xeb\x8a\xc4\x03l\x87\xd0G\x11\x05\xb4\xfap\xe0\x11\xd3W\xcf.\xdb\xc1ye(\xd5A\xc9\x8ds\x83\xaf\xab$\xfd\xf5\x93\xc4,\x7fX\xcf\'\xd7\xa5\xb1\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xb2{\x8c\xa7`a42\xf5\xaa\x87\xa0\rQQB\x1ah*\x07y\xd83k\xf3\xf3\xb6\x8b\x01\xde\xe7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xafw\x92n\x02V\xb3:\x8d\xd4P\x19~\x0b\xa2\x8e\xf5\xaf\'\x1f\xc3\xd8K\xfd\xfd\xc9\x837\x84\x00\xe7K\x06#E\xda\x86B\xf7Q\xb3\xb2\x97\xea\xbd\x96\xdb\xcc\xa1\x91\x87\x04\x1fHqJ\xa2\xbc2\xcc\x04\xacn\x9e\xeb\x9da\xc9R\xcdA\xccD\x1f\t\x99O\x00\xfe\xa9<(W\xb1|\xb3=Rx\xab\xe1\xd3&b\xcd3\x00\x00\x00\x00\x07\x02\x807\x11|\xa3\x80' # noqa: E501
|
||||
"GENESIS_BLOCK": b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x883|\xde\xeb0\x9e[1:\x97\xe1Jp\x1e\x85|r9 Q\x81*\x058\xe9\xef\xeb\x89\x0c\x8b\xaft\xe6T\xfd\xf2W[\x01\xf0\x16\xc4\n`\xeb\xbd%\x84m\x82\xb5\xc6\xaa\xe8p\x8e\x8f\xa1\x814Rw\xa2\x9b\xce\xd2\x8a\x94\xfc\xb8\x90o\\\xad:;\x12\x9f\xac\xe2f\xe0\x13\x00\xe9]\x83\xa3\xb6\x8f\x15\x8ds\x91\xdd\x12\x00\x00\x00\x90\x9b2K\xbd\x14\xbc\x85/\x06\xad\xc4\xa0%H\xd5\x81\x0b\xe8\x0e\x0c+\xa3W^(\x84e\xb5\xa9I*\xc7L\xb2\x83L\xc4\xca\xe2\xa9\x9a\xc6\xba\xc7\x9b"s\xd0O\xf9\x06&,4\x82tX-I[\xaf\t6Na\x01\x19!;\xbb\x13\x0b\x9bT\x19\x8c\xf6Q2q\x19\taYhL\xc84(\xaa6\xfb\x9aA\xfc\x06\xc7#\xab\xe0\x1c\x843\xf3\xf7k\xd0\xa9\x087N\x1d\x92\x97<o3+\x10J\xf5\xad\xdc\xfb\xa5\x8b[t\xea\xcd\x00uB\xcf\x1e\xf8\xb7\x15\xd6=\x8b;\xc0\xf0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x05\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\r\xc0\x88F\r6\x84\x9d\x85\xc1\xa2\x9aq\xf7\x1cFXm\xf5L\x9f \xbb\x1d\x10\x97\x84\xb5$\x01\xc9^\x7f2\xfb\xee\xeb\x85\xa6\x02\xe3l\xba\xee+\xa25\x00\xd4\xe1\x90/\x08\x97\xef\x18#\xb7\xe5+\xb2\xb9\xc8\xb9\xce\xe1\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06\xf8\xa8}\xd4_\x98QR\x14"\x06<\xa4;\x9f2\xd3\xed\xd0mK\xf7\xfa~<\x88B\x8b\xd4F\xdcHp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\xc1\xef\x962\x87V\xc4\xd5\xd5AL\x12\x1c\xe4|{\xc0\x7f{.\xe98\x93%\x08:\xa66\xe7\x80Q\x00\x00\x01\xd1\xa9J \x00+[\xbbW\xc5\xdaVh\x93\x16Q\xaa\r\x12\t"\xe3?:fk\xfd\x1f\x82q\xd0\r\x02U\xcc\xf9\xdb\x00\x00\x00\x00\xa3\xaa\xa0\xe0\xediiw+\xae\xecY\r{4\x9f\xc5\x82\'"\x9d\x8dI\x03Y{\x82\xd7pY\xb5\xd7\xc8\x0c-9\xff\xb141\xa5\xee\x17\x01\x1b\x0b&t\xa9\xf6\xe9\xeb\x8a\xc4\x03l\x87\xd0G\x11\x05\xb4\xfap\xe0\x11\xd3W\xcf.\xdb\xc1ye(\xd5A\xc9\x8ds\x83\xaf\xab$\xfd\xf5\x93\xc4,\x7fX\xcf\'\xd7\xa5\xb1\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xb2{\x8c\xa7`a42\xf5\xaa\x87\xa0\rQQB\x1ah*\x07y\xd83k\xf3\xf3\xb6\x8b\x01\xde\xe7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xafw\x92n\x02V\xb3:\x8d\xd4P\x19~\x0b\xa2\x8e\xf5\xaf\'\x1f\xc3\xd8K\xfd\xfd\xc9\x837\x84\x00\xe7K\x06#E\xda\x86B\xf7Q\xb3\xb2\x97\xea\xbd\x96\xdb\xcc\xa1\x91\x87\x04\x1fHqJ\xa2\xbc2\xcc\x04\xacn\x9e\xeb\x9da\xc9R\xcdA\xccD\x1f\t\x99O\x00\xfe\xa9<(W\xb1|\xb3=Rx\xab\xe1\xd3&b\xcd3\x00\x00\x00\x00\x07\x02\x807\x11|\xa3\x80', # noqa: E501
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -94,12 +94,10 @@ class BlockTools:
|
||||
temp_dir = plot_dir / "tmp"
|
||||
mkdir(temp_dir)
|
||||
args = Namespace()
|
||||
args.sk_seed = std_hash(b"").hex()
|
||||
# Can't go much lower than 18, since plots start having no solutions
|
||||
args.size = 18
|
||||
# Uses many plots for testing, in order to guarantee proofs of space at every height
|
||||
args.num = 40
|
||||
args.index = 0
|
||||
args.buffer = 32
|
||||
args.farmer_public_key = bytes(self.farmer_pk).hex()
|
||||
args.pool_public_key = bytes(self.pool_pk).hex()
|
||||
@ -146,7 +144,7 @@ class BlockTools:
|
||||
if len(self.pool_pubkeys) == 0 or len(farmer_pubkeys) == 0:
|
||||
raise RuntimeError("Keys not generated. Run `chia generate keys`")
|
||||
_, self.plots, _, _ = load_plots(
|
||||
{}, set(), farmer_pubkeys, self.pool_pubkeys, root_path
|
||||
{}, {}, farmer_pubkeys, self.pool_pubkeys, root_path
|
||||
)
|
||||
|
||||
def get_plot_signature(
|
||||
@ -252,8 +250,7 @@ class BlockTools:
|
||||
else:
|
||||
block1 = block_list[0]
|
||||
timestamp1 = uint64(
|
||||
block1.header.data.timestamp
|
||||
- test_constants.BLOCK_TIME_TARGET
|
||||
block1.header.data.timestamp - test_constants.BLOCK_TIME_TARGET
|
||||
)
|
||||
iters1 = uint64(0)
|
||||
timestamp2 = block_list[height2].header.data.timestamp
|
||||
|
@ -1,10 +1,10 @@
|
||||
SERVICES_FOR_GROUP = {
|
||||
"all": "chia_harvester chia_timelord_launcher chia_timelord chia_farmer chia_full_node chia-wallet".split(),
|
||||
"all": "chia_harvester chia_timelord_launcher chia_timelord chia_farmer chia_full_node chia_wallet".split(),
|
||||
"node": "chia_full_node".split(),
|
||||
"harvester": "chia_harvester".split(),
|
||||
"farmer": "chia_harvester chia_farmer chia_full_node chia-wallet".split(),
|
||||
"farmer": "chia_harvester chia_farmer chia_full_node chia_wallet".split(),
|
||||
"timelord": "chia_timelord_launcher chia_timelord chia_full_node".split(),
|
||||
"wallet-server": "chia-wallet chia_full_node".split(),
|
||||
"wallet": "chia_wallet chia_full_node".split(),
|
||||
"introducer": "chia_introducer".split(),
|
||||
"simulator": "chia_full_node_simulator".split(),
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ class WalletNode:
|
||||
self.root_path, f"{self.config['database_path']}-{db_path_key_suffix}"
|
||||
)
|
||||
mkdir(path.parent)
|
||||
|
||||
self.wallet_state_manager = await WalletStateManager.create(
|
||||
private_key, self.config, path, self.constants
|
||||
)
|
||||
@ -186,7 +187,8 @@ class WalletNode:
|
||||
|
||||
async def _await_closed(self):
|
||||
if self.sync_generator_task is not None:
|
||||
await self.sync_generator_task.aclose()
|
||||
async for _ in self.sync_generator_task:
|
||||
pass
|
||||
if self.wallet_state_manager is None or self.backup_initialized is False:
|
||||
return
|
||||
await self.wsm_close_task
|
||||
@ -242,7 +244,11 @@ class WalletNode:
|
||||
self.server.push_message(msg)
|
||||
|
||||
async def _messages_to_resend(self) -> List[OutboundMessage]:
|
||||
if self.wallet_state_manager is None or self.backup_initialized is False:
|
||||
if (
|
||||
self.wallet_state_manager is None
|
||||
or self.backup_initialized is False
|
||||
or self._shut_down
|
||||
):
|
||||
return []
|
||||
messages: List[OutboundMessage] = []
|
||||
|
||||
|
@ -709,7 +709,10 @@ class WalletStateManager:
|
||||
if header_block is not None:
|
||||
if not await self.validate_header_block(block, header_block):
|
||||
return ReceiveBlockResult.INVALID_BLOCK
|
||||
if (block.height + 1) % self.constants.DIFFICULTY_EPOCH == self.constants.DIFFICULTY_DELAY:
|
||||
if (
|
||||
(block.height + 1) % self.constants.DIFFICULTY_EPOCH
|
||||
== self.constants.DIFFICULTY_DELAY
|
||||
):
|
||||
assert header_block.challenge.new_work_difficulty is not None
|
||||
self.difficulty_resets_prev[
|
||||
block.header_hash
|
||||
@ -806,9 +809,7 @@ class WalletStateManager:
|
||||
)
|
||||
else:
|
||||
# The rest of the blocks of epoch (using new difficulty and min iters)
|
||||
height2 = (
|
||||
curr.height - (curr.height % self.constants.DIFFICULTY_EPOCH) - 1
|
||||
)
|
||||
height2 = curr.height - (curr.height % self.constants.DIFFICULTY_EPOCH) - 1
|
||||
height1 = height2 - self.constants.DIFFICULTY_EPOCH
|
||||
assert height2 > 0
|
||||
|
||||
@ -824,10 +825,7 @@ class WalletStateManager:
|
||||
assert iters2 is not None
|
||||
min_iters_precise = uint64(
|
||||
(iters2 - iters1)
|
||||
// (
|
||||
self.constants.DIFFICULTY_EPOCH
|
||||
* self.constants.MIN_ITERS_PROPORTION
|
||||
)
|
||||
// (self.constants.DIFFICULTY_EPOCH * self.constants.MIN_ITERS_PROPORTION)
|
||||
)
|
||||
# Truncates to only 12 bits plus 0s. This prevents grinding attacks.
|
||||
return uint64(
|
||||
@ -996,7 +994,7 @@ class WalletStateManager:
|
||||
|
||||
def validate_select_proofs(
|
||||
self,
|
||||
all_proof_hashes: List[Tuple[bytes32, Optional[Tuple[uint64, uint64]]]],
|
||||
all_proof_hashes: List[Tuple[bytes32, Optional[uint64], Optional[uint64]]],
|
||||
heights: List[uint32],
|
||||
cached_blocks: Dict[bytes32, Tuple[BlockRecord, HeaderBlock, Optional[bytes]]],
|
||||
potential_header_hashes: Dict[uint32, bytes32],
|
||||
@ -1105,8 +1103,7 @@ class WalletStateManager:
|
||||
|
||||
if (
|
||||
height
|
||||
< self.constants.DIFFICULTY_EPOCH
|
||||
+ self.constants.DIFFICULTY_DELAY
|
||||
< self.constants.DIFFICULTY_EPOCH + self.constants.DIFFICULTY_DELAY
|
||||
):
|
||||
min_iters = self.constants.MIN_ITERS_STARTING
|
||||
else:
|
||||
@ -1125,11 +1122,11 @@ class WalletStateManager:
|
||||
|
||||
height1 = height2 - self.constants.DIFFICULTY_EPOCH
|
||||
if height1 == -1:
|
||||
iters1 = uint64(0)
|
||||
iters1: Optional[uint64] = uint64(0)
|
||||
else:
|
||||
iters1 = all_proof_hashes[height1][2]
|
||||
assert iters1 is not None
|
||||
iters2 = all_proof_hashes[height2][2]
|
||||
assert iters1 is not None
|
||||
assert iters2 is not None
|
||||
|
||||
min_iters = uint64(
|
||||
|
@ -221,7 +221,7 @@ class WalletStore:
|
||||
regular_rows = await cursor_regular_coins.fetchall()
|
||||
await cursor_regular_coins.close()
|
||||
|
||||
for row in coinbase_rows + regular_rows:
|
||||
for row in list(coinbase_rows) + list(regular_rows):
|
||||
coin = Coin(
|
||||
bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
|
||||
)
|
||||
@ -334,7 +334,7 @@ class WalletStore:
|
||||
if br.height > max_height:
|
||||
max_height = br.height
|
||||
# Makes sure there's exactly one block per height
|
||||
assert max_height == len(rows) - 1
|
||||
assert max_height == len(list(rows)) - 1
|
||||
return hash_to_br
|
||||
|
||||
async def add_block_record(self, block_record: BlockRecord, in_lca_path: bool):
|
||||
|
@ -146,7 +146,7 @@ class TestCCTrades:
|
||||
|
||||
await time_out_assert(15, cc_wallet_2.get_confirmed_balance, 30)
|
||||
await time_out_assert(15, cc_wallet_2.get_unconfirmed_balance, 30)
|
||||
trade: TradeStatus = await trade_manager_0.get_trade_by_id(trade_offer.trade_id)
|
||||
trade = await trade_manager_0.get_trade_by_id(trade_offer.trade_id)
|
||||
assert TradeStatus(trade.status) is TradeStatus.CONFIRMED
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@ -271,13 +271,15 @@ class TestCCTrades:
|
||||
wallet_node_b.wallet_state_manager, wallet_b, colour
|
||||
)
|
||||
|
||||
offer = {1: -30, 4: 60}
|
||||
offer_dict = {1: -30, 4: 60}
|
||||
file = "test_offer_file.offer"
|
||||
file_path = Path(file)
|
||||
if file_path.exists():
|
||||
file_path.unlink()
|
||||
|
||||
success, offer, error = await trade_manager_b.create_offer_for_ids(offer, file)
|
||||
success, offer, error = await trade_manager_b.create_offer_for_ids(
|
||||
offer_dict, file
|
||||
)
|
||||
|
||||
success, trade_a, reason = await trade_manager_a.respond_to_offer(file_path)
|
||||
|
||||
@ -288,11 +290,15 @@ class TestCCTrades:
|
||||
await time_out_assert(15, cc_b_4.get_confirmed_balance, 60)
|
||||
|
||||
async def assert_func():
|
||||
assert trade_a is not None
|
||||
trade = await trade_manager_a.get_trade_by_id(trade_a.trade_id)
|
||||
assert trade is not None
|
||||
return trade.status
|
||||
|
||||
async def assert_func_b():
|
||||
assert offer is not None
|
||||
trade = await trade_manager_b.get_trade_by_id(offer.trade_id)
|
||||
assert trade is not None
|
||||
return trade.status
|
||||
|
||||
await time_out_assert(15, assert_func, TradeStatus.CONFIRMED.value)
|
||||
@ -342,6 +348,7 @@ class TestCCTrades:
|
||||
assert spendable_chia == spendable_after_cancel_1
|
||||
|
||||
trade_a = await trade_manager_a.get_trade_by_id(trade_offer.trade_id)
|
||||
assert trade_a is not None
|
||||
assert trade_a.status == TradeStatus.CANCELED.value
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@ -392,7 +399,9 @@ class TestCCTrades:
|
||||
# Spendable should be the same as it was before making offer 1
|
||||
|
||||
async def get_status():
|
||||
assert trade_offer is not None
|
||||
trade_a = await trade_manager_a.get_trade_by_id(trade_offer.trade_id)
|
||||
assert trade_a is not None
|
||||
return trade_a.status
|
||||
|
||||
await time_out_assert(15, get_status, TradeStatus.CANCELED.value)
|
||||
|
@ -1,4 +1,26 @@
|
||||
. _chia-common
|
||||
_kill_servers() {
|
||||
PROCS=`ps -e | grep -E 'chia|vdf_client' -v "chia-start-sim" | awk '!/grep/' | awk '{print $1}'`
|
||||
if [ -n "$PROCS" ]; then
|
||||
echo "$PROCS" | xargs -L1 kill -KILL
|
||||
fi
|
||||
}
|
||||
|
||||
_kill_servers
|
||||
|
||||
BG_PIDS=""
|
||||
_run_bg_cmd() {
|
||||
"$@" &
|
||||
BG_PIDS="$BG_PIDS $!"
|
||||
}
|
||||
|
||||
|
||||
_term() {
|
||||
echo "Caught TERM or INT signal, killing all servers."
|
||||
for PID in $BG_PIDS; do
|
||||
kill -TERM "$PID"
|
||||
done
|
||||
_kill_servers
|
||||
}
|
||||
|
||||
echo "Starting local blockchain simulation. Runs a local introducer and chia system."
|
||||
echo "Note that this simulation will not work if connected to external nodes."
|
@ -111,9 +111,7 @@ class TestCoinStore:
|
||||
async def test_basic_reorg(self):
|
||||
initial_block_count = 20
|
||||
reorg_length = 15
|
||||
blocks = bt.get_consecutive_blocks(
|
||||
test_constants, initial_block_count, [], 9
|
||||
)
|
||||
blocks = bt.get_consecutive_blocks(test_constants, initial_block_count, [], 9)
|
||||
db_path = Path("blockchain_test.db")
|
||||
if db_path.exists():
|
||||
db_path.unlink()
|
||||
|
@ -865,10 +865,7 @@ class TestWalletProtocol:
|
||||
hashes = msgs[0].message.data.hashes
|
||||
assert len(hashes) >= len(blocks_list) - 2
|
||||
for i in range(len(hashes)):
|
||||
if (
|
||||
i % test_constants.DIFFICULTY_EPOCH
|
||||
== test_constants.DIFFICULTY_DELAY
|
||||
):
|
||||
if i % test_constants.DIFFICULTY_EPOCH == test_constants.DIFFICULTY_DELAY:
|
||||
assert hashes[i][1] is not None
|
||||
elif i > 0:
|
||||
assert hashes[i][1] is None
|
||||
|
@ -111,8 +111,13 @@ class TestRpc:
|
||||
res_2 = await client_2.get_plots()
|
||||
assert len(res_2["plots"]) == num_plots
|
||||
|
||||
print(await client_2.get_plot_directories())
|
||||
assert len(await client_2.get_plot_directories()) == 1
|
||||
|
||||
await client_2.add_plot_directory(str(plot_dir))
|
||||
|
||||
assert len(await client_2.get_plot_directories()) == 2
|
||||
|
||||
res_2 = await client_2.get_plots()
|
||||
assert len(res_2["plots"]) == num_plots + 1
|
||||
|
||||
@ -120,6 +125,10 @@ class TestRpc:
|
||||
res_3 = await client_2.get_plots()
|
||||
assert len(res_3["plots"]) == num_plots
|
||||
|
||||
await client_2.remove_plot_directory(str(plot_dir))
|
||||
print(await client_2.get_plot_directories())
|
||||
assert len(await client_2.get_plot_directories()) == 1
|
||||
|
||||
except AssertionError:
|
||||
# Checks that the RPC manages to stop the node
|
||||
client.close()
|
||||
|
@ -260,7 +260,11 @@ async def setup_harvester(port, farmer_port, consensus_constants: ConsensusConst
|
||||
await run_task
|
||||
|
||||
|
||||
async def setup_farmer(port, consensus_constants: ConsensusConstants, full_node_port: Optional[uint16] = None):
|
||||
async def setup_farmer(
|
||||
port,
|
||||
consensus_constants: ConsensusConstants,
|
||||
full_node_port: Optional[uint16] = None,
|
||||
):
|
||||
config = load_config(bt.root_path, "config.yaml", "farmer")
|
||||
config_pool = load_config(bt.root_path, "config.yaml", "pool")
|
||||
|
||||
@ -357,7 +361,9 @@ async def setup_vdf_clients(port):
|
||||
await kill_processes()
|
||||
|
||||
|
||||
async def setup_timelord(port, full_node_port, sanitizer, consensus_constants: ConsensusConstants):
|
||||
async def setup_timelord(
|
||||
port, full_node_port, sanitizer, consensus_constants: ConsensusConstants
|
||||
):
|
||||
config = load_config(bt.root_path, "config.yaml", "timelord")
|
||||
config["sanitizer_mode"] = sanitizer
|
||||
if sanitizer:
|
||||
@ -424,12 +430,16 @@ async def setup_two_nodes(consensus_constants: ConsensusConstants):
|
||||
await _teardown_nodes(node_iters)
|
||||
|
||||
|
||||
async def setup_node_and_wallet(consensus_constants: ConsensusConstants, starting_height=None):
|
||||
async def setup_node_and_wallet(
|
||||
consensus_constants: ConsensusConstants, starting_height=None
|
||||
):
|
||||
node_iters = [
|
||||
setup_full_node(
|
||||
consensus_constants, "blockchain_test.db", 21234, simulator=False
|
||||
),
|
||||
setup_wallet_node(21235, consensus_constants, None, starting_height=starting_height),
|
||||
setup_wallet_node(
|
||||
21235, consensus_constants, None, starting_height=starting_height
|
||||
),
|
||||
]
|
||||
|
||||
full_node, s1 = await node_iters[0].__anext__()
|
||||
@ -459,7 +469,11 @@ async def setup_simulators_and_wallets(
|
||||
seed = bytes(uint32(index))
|
||||
port = 55000 + index
|
||||
wlt = setup_wallet_node(
|
||||
port, consensus_constants, None, key_seed=seed, starting_height=starting_height
|
||||
port,
|
||||
consensus_constants,
|
||||
None,
|
||||
key_seed=seed,
|
||||
starting_height=starting_height,
|
||||
)
|
||||
wallets.append(await wlt.__anext__())
|
||||
node_iters.append(wlt)
|
||||
|
Loading…
Reference in New Issue
Block a user