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:
Mariano Sorgente 2020-07-24 20:46:18 -07:00 committed by Gene Hoffman
parent 3f6401d4c1
commit 700eaad9e0
70 changed files with 911 additions and 442 deletions

View File

@ -7,6 +7,7 @@ jobs:
build: build:
name: MacOS-latest on Pyton 3.7 name: MacOS-latest on Pyton 3.7
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 4 max-parallel: 4

View File

@ -7,6 +7,7 @@ jobs:
build: build:
name: Ubuntu-latest on Python 3.7 + 3.8 name: Ubuntu-latest on Python 3.7 + 3.8
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 4 max-parallel: 4

View File

@ -6,6 +6,7 @@ jobs:
build: build:
name: Windows installer on 3.7 name: Windows installer on 3.7
runs-on: [windows-latest] runs-on: [windows-latest]
timeout-minutes: 60
steps: steps:
- name: Cancel previous runs on the same branch - name: Cancel previous runs on the same branch

View File

@ -29,6 +29,7 @@ jobs:
name: Lint Code Base name: Lint Code Base
# Set the agent to run on # Set the agent to run on
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60
################## ##################
# Load all steps # # Load all steps #

View File

@ -6,6 +6,7 @@ jobs:
upload_source_dist: upload_source_dist:
name: Lint and Upload source distribution name: Lint and Upload source distribution
runs-on: [ubuntu-latest] runs-on: [ubuntu-latest]
timeout-minutes: 60
steps: steps:
- name: Cancel previous runs on the same branch - name: Cancel previous runs on the same branch

View File

@ -9,18 +9,35 @@ for setuptools_scm/PEP 440 reasons.
## [Unreleased] ## [Unreleased]
### Added ### 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. - Windows BLS Signature library now uses libsodium for additional security.
- Wheels for ARM64/aarch64 also build for python 3.7. - 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 ### 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
- 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. - Addressed pre-Haswell Windows signatures failing.
- MacOS, Linux x64, and Linux aarch64 were not correctly compiling libsodium in - MacOS, Linux x64, and Linux aarch64 were not correctly compiling libsodium in
the blspy/bls-signatures library. the blspy/bls-signatures library.
- Removed outdated "200 plots" language from Plot tab. - Removed outdated "200 plots" language from Plot tab.
- Fixed spelling error for "folder" on Plot tab. - Fixed spelling error for "folder" on Plot tab.
- Various node dependency security vulnerabilities have been fixed. - 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 ## [1.0beta8] aka Beta 1.8 - 2020-07-16

View File

@ -160,7 +160,7 @@ wallet_exe = EXE(wallet_pyz,
wallet.scripts, wallet.scripts,
[], [],
exclude_binaries=True, exclude_binaries=True,
name='wallet_server', name='start_wallet',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False) strip=False)

View File

@ -163,7 +163,7 @@ wallet_exe = EXE(wallet_pyz,
wallet.scripts, wallet.scripts,
[], [],
exclude_binaries=True, exclude_binaries=True,
name='wallet_server', name='start_wallet',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False) strip=False)

View File

@ -145,7 +145,7 @@ const closeDaemon = callback => {
if (!called_cb) { if (!called_cb) {
callback(); callback();
} }
}, 15000); }, 20000);
}; };
const exitPyProc = e => {}; const exitPyProc = e => {};
@ -214,29 +214,17 @@ const createWindow = () => {
// } // }
mainWindow.on("close", e => { mainWindow.on("close", e => {
if (decidedToClose) { 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; return;
} }
e.preventDefault(); e.preventDefault();
var choice = require("electron").dialog.showMessageBoxSync({ var choice = require("electron").dialog.showMessageBoxSync({
type: "question", type: "question",
buttons: ["Yes", "No"], buttons: ["No", "Yes"],
title: "Confirm", title: "Confirm",
message: message:
"Are you sure you want to quit? GUI Plotting and farming will stop." "Are you sure you want to quit? GUI Plotting and farming will stop."
}); });
if (choice == 1) { if (choice == 0) {
return; return;
} }
mainWindow.loadURL(closingUrl); mainWindow.loadURL(closingUrl);

View File

@ -7,7 +7,7 @@ import {
} from "../modules/daemon_messages"; } from "../modules/daemon_messages";
import { handle_message } from "./middleware_api"; import { handle_message } from "./middleware_api";
import { import {
service_wallet_server, service_wallet,
service_full_node, service_full_node,
service_simulator, service_simulator,
service_plotter service_plotter
@ -38,10 +38,10 @@ const socketMiddleware = () => {
let start_wallet, start_node; let start_wallet, start_node;
if (config.local_test) { if (config.local_test) {
start_wallet = startServiceTest(service_wallet_server); start_wallet = startServiceTest(service_wallet);
start_node = startService(service_simulator); start_node = startService(service_simulator);
} else { } else {
start_wallet = startService(service_wallet_server); start_wallet = startService(service_wallet);
start_node = startService(service_full_node); start_node = startService(service_full_node);
} }
store.dispatch(isServiceRunning(service_plotter)); store.dispatch(isServiceRunning(service_plotter));

View File

@ -15,7 +15,7 @@ import {
import { offerParsed, resetTrades } from "../modules/TradeReducer"; import { offerParsed, resetTrades } from "../modules/TradeReducer";
import { openDialog } from "../modules/dialogReducer"; import { openDialog } from "../modules/dialogReducer";
import { import {
service_wallet_server, service_wallet,
service_full_node, service_full_node,
service_simulator, service_simulator,
service_farmer, service_farmer,
@ -35,6 +35,7 @@ import {
} from "../modules/farmerMessages"; } from "../modules/farmerMessages";
import { import {
getPlots, getPlots,
getPlotDirectories,
pingHarvester, pingHarvester,
refreshPlots refreshPlots
} from "../modules/harvesterMessages"; } from "../modules/harvesterMessages";
@ -55,7 +56,7 @@ function sleep(ms) {
async function ping_wallet(store) { async function ping_wallet(store) {
store.dispatch(pingWallet()); store.dispatch(pingWallet());
await sleep(300); await sleep(1000);
const state = store.getState(); const state = store.getState();
const wallet_connected = state.daemon_state.wallet_connected; const wallet_connected = state.daemon_state.wallet_connected;
if (!wallet_connected) { if (!wallet_connected) {
@ -65,7 +66,7 @@ async function ping_wallet(store) {
async function ping_full_node(store) { async function ping_full_node(store) {
store.dispatch(pingFullNode()); store.dispatch(pingFullNode());
await sleep(300); await sleep(1000);
const state = store.getState(); const state = store.getState();
const node_connected = state.daemon_state.full_node_connected; const node_connected = state.daemon_state.full_node_connected;
if (!node_connected) { if (!node_connected) {
@ -75,7 +76,7 @@ async function ping_full_node(store) {
async function ping_farmer(store) { async function ping_farmer(store) {
store.dispatch(pingFarmer()); store.dispatch(pingFarmer());
await sleep(300); await sleep(1000);
const state = store.getState(); const state = store.getState();
const farmer_connected = state.daemon_state.farmer_connected; const farmer_connected = state.daemon_state.farmer_connected;
if (!farmer_connected) { if (!farmer_connected) {
@ -85,7 +86,7 @@ async function ping_farmer(store) {
async function ping_harvester(store) { async function ping_harvester(store) {
store.dispatch(pingHarvester()); store.dispatch(pingHarvester());
await sleep(300); await sleep(1000);
const state = store.getState(); const state = store.getState();
const harvester_connected = state.daemon_state.harvester_connected; const harvester_connected = state.daemon_state.harvester_connected;
if (!harvester_connected) { if (!harvester_connected) {
@ -145,6 +146,7 @@ export const refreshAllState = dispatch => {
dispatch(getLatestChallenges()); dispatch(getLatestChallenges());
dispatch(getFarmerConnections()); dispatch(getFarmerConnections());
dispatch(getPlots()); dispatch(getPlots());
dispatch(getPlotDirectories());
dispatch(isServiceRunning(service_plotter)); dispatch(isServiceRunning(service_plotter));
dispatch(get_all_trades()); dispatch(get_all_trades());
}; };
@ -152,7 +154,7 @@ export const refreshAllState = dispatch => {
export const handle_message = (store, payload) => { export const handle_message = (store, payload) => {
store.dispatch(incomingMessage(payload)); store.dispatch(incomingMessage(payload));
if (payload.command === "ping") { if (payload.command === "ping") {
if (payload.origin === service_wallet_server) { if (payload.origin === service_wallet) {
store.dispatch(get_connection_info()); store.dispatch(get_connection_info());
store.dispatch(format_message("get_public_keys", {})); store.dispatch(format_message("get_public_keys", {}));
} else if (payload.origin === service_full_node) { } else if (payload.origin === service_full_node) {
@ -163,7 +165,6 @@ export const handle_message = (store, payload) => {
store.dispatch(getLatestChallenges()); store.dispatch(getLatestChallenges());
store.dispatch(getFarmerConnections()); store.dispatch(getFarmerConnections());
} else if (payload.origin === service_harvester) { } else if (payload.origin === service_harvester) {
store.dispatch(getPlots());
} }
} else if (payload.command === "delete_key") { } else if (payload.command === "delete_key") {
if (payload.data.success) { if (payload.data.success) {
@ -244,7 +245,7 @@ export const handle_message = (store, payload) => {
} else if (payload.command === "start_service") { } else if (payload.command === "start_service") {
const service = payload.data.service; const service = payload.data.service;
if (payload.data.success) { if (payload.data.success) {
if (service === service_wallet_server) { if (service === service_wallet) {
ping_wallet(store); ping_wallet(store);
} else if (service === service_full_node) { } else if (service === service_full_node) {
ping_full_node(store); ping_full_node(store);
@ -258,7 +259,7 @@ export const handle_message = (store, payload) => {
track_progress(store, payload.data.out_file); track_progress(store, payload.data.out_file);
} }
} else if (payload.data.error === "already running") { } else if (payload.data.error === "already running") {
if (service === service_wallet_server) { if (service === service_wallet) {
ping_wallet(store); ping_wallet(store);
} else if (service === service_full_node) { } else if (service === service_full_node) {
ping_full_node(store); ping_full_node(store);

View File

@ -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 addTrade = trade => ({ type: "TRADE_ADDED", trade });
export const resetTrades = () => ({ type: "RESET_TRADE" }); export const resetTrades = () => ({ type: "RESET_TRADE" });
@ -53,7 +53,7 @@ export const tradeReducer = (state = { ...initial_state }, action) => {
let trade; let trade;
switch (action.type) { switch (action.type) {
case "INCOMING_MESSAGE": case "INCOMING_MESSAGE":
if (action.message.origin !== service_wallet_server) { if (action.message.origin !== service_wallet) {
return state; return state;
} }

View File

@ -1,5 +1,5 @@
import { import {
service_wallet_server, service_wallet,
service_full_node, service_full_node,
service_simulator, service_simulator,
service_daemon, service_daemon,
@ -43,7 +43,7 @@ export const daemonReducer = (state = { ...initial_state }, action) => {
state.full_node_running = true; state.full_node_running = true;
} else if (service === service_simulator) { } else if (service === service_simulator) {
state.full_node_running = true; state.full_node_running = true;
} else if (service === service_wallet_server) { } else if (service === service_wallet) {
state.wallet_running = true; state.wallet_running = true;
} else if (service === service_farmer) { } else if (service === service_farmer) {
state.farmer_running = true; state.farmer_running = true;
@ -56,7 +56,7 @@ export const daemonReducer = (state = { ...initial_state }, action) => {
state.full_node_connected = true; state.full_node_connected = true;
} else if (origin === service_simulator) { } else if (origin === service_simulator) {
state.full_node_connected = true; state.full_node_connected = true;
} else if (origin === service_wallet_server) { } else if (origin === service_wallet) {
state.wallet_connected = true; state.wallet_connected = true;
} else if (origin === service_farmer) { } else if (origin === service_farmer) {
state.farmer_connected = true; state.farmer_connected = true;
@ -70,7 +70,7 @@ export const daemonReducer = (state = { ...initial_state }, action) => {
state.plotter_running = data.is_running; state.plotter_running = data.is_running;
} else if (service === service_full_node) { } else if (service === service_full_node) {
state.full_node_running = data.is_running; state.full_node_running = data.is_running;
} else if (service === service_wallet_server) { } else if (service === service_wallet) {
state.wallet_running = data.is_running; state.wallet_running = data.is_running;
} else if (service === service_farmer) { } else if (service === service_farmer) {
state.farmer_running = data.is_running; state.farmer_running = data.is_running;

View File

@ -9,7 +9,8 @@ const initial_state = {
harvester: { harvester: {
plots: [], plots: [],
not_found_filenames: [], 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; return state;
} }
if (command === "get_plot_directories") {
if (data.success !== true) {
return state;
}
state.harvester.plot_directories = data.directories;
return state;
}
return state; return state;
default: default:
return state; return state;

View File

@ -21,6 +21,13 @@ export const getPlots = () => {
return action; return action;
}; };
export const getPlotDirectories = () => {
var action = harvesterMessage();
action.message.command = "get_plot_directories";
action.message.data = {};
return action;
};
export const deletePlot = filename => { export const deletePlot = filename => {
var action = harvesterMessage(); var action = harvesterMessage();
action.message.command = "delete_plot"; action.message.command = "delete_plot";
@ -41,3 +48,10 @@ export const addPlotDirectory = dirname => {
action.message.data = { dirname }; action.message.data = { dirname };
return action; return action;
}; };
export const removePlotDirectory = dirname => {
var action = harvesterMessage();
action.message.command = "remove_plot_directory";
action.message.data = { dirname };
return action;
};

View File

@ -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) => ({ export const Wallet = (id, name, type, data) => ({
id: id, id: id,
@ -101,7 +101,7 @@ export const incomingReducer = (state = { ...initial_state }, action) => {
} }
return state; return state;
case "INCOMING_MESSAGE": case "INCOMING_MESSAGE":
if (action.message.origin !== service_wallet_server) { if (action.message.origin !== service_wallet) {
return state; return state;
} }

View File

@ -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 { openProgress, closeProgress } from "./progressReducer";
import { refreshAllState } from "../middleware/middleware_api"; 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 { openDialog } from "./dialogReducer";
import { createState } from "./createWalletReducer"; import { createState } from "./createWalletReducer";
import {
addPlotDirectory,
getPlotDirectories,
removePlotDirectory,
getPlots,
refreshPlots
} from "./harvesterMessages";
export const clearSend = () => { export const clearSend = () => {
var action = { var action = {
@ -16,7 +28,7 @@ export const clearSend = () => {
export const walletMessage = () => ({ export const walletMessage = () => ({
type: "OUTGOING_MESSAGE", type: "OUTGOING_MESSAGE",
message: { message: {
destination: service_wallet_server destination: service_wallet
} }
}); });
@ -116,9 +128,16 @@ export const add_new_key_action = mnemonic => {
dispatch(closeProgress()); dispatch(closeProgress());
if (response.data.success) { if (response.data.success) {
// Go to wallet // Go to wallet
dispatch(resetMnemonic());
dispatch(format_message("get_public_keys", {})); dispatch(format_message("get_public_keys", {}));
refreshAllState(dispatch); refreshAllState(dispatch);
} else { } 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; const error = response.data.error;
dispatch(openDialog("Error", error)); dispatch(openDialog("Error", error));
} }
@ -133,9 +152,18 @@ export const add_and_skip_backup = mnemonic => {
dispatch(closeProgress()); dispatch(closeProgress());
if (response.data.success) { if (response.data.success) {
// Go to wallet // Go to wallet
dispatch(resetMnemonic());
dispatch(format_message("get_public_keys", {})); dispatch(format_message("get_public_keys", {}));
refreshAllState(dispatch); refreshAllState(dispatch);
} else { } 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; const error = response.data.error;
dispatch(openDialog("Error", error)); dispatch(openDialog("Error", error));
} }
@ -154,9 +182,16 @@ export const add_and_restore_from_backup = (mnemonic, file_path) => {
dispatch(closeProgress()); dispatch(closeProgress());
if (response.data.success) { if (response.data.success) {
// Go to wallet // Go to wallet
dispatch(resetMnemonic());
dispatch(format_message("get_public_keys", {})); dispatch(format_message("get_public_keys", {}));
refreshAllState(dispatch); refreshAllState(dispatch);
} else { } 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; const error = response.data.error;
dispatch(openDialog("Error", error)); dispatch(openDialog("Error", error));
} }
@ -423,3 +458,41 @@ export const incomingMessage = message => ({
type: "INCOMING_MESSAGE", type: "INCOMING_MESSAGE",
message: 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));
}
}
);
};
};

View File

@ -1,9 +1,10 @@
export const wordChanged = msg => ({ type: "MNEMONIC_TYPING" }); export const wordChanged = msg => ({ type: "MNEMONIC_TYPING" });
export const resetMnemonic = msg => ({ type: "RESET_MNEMONIC" }); export const resetMnemonic = msg => ({ type: "RESET_MNEMONIC" });
export const setIncorrectWord = word => ({ type: "SET_INCORRECT_WORD", word });
const initial_state = { const initial_state = {
mnemonic_input: new Array("24"), mnemonic_input: new Array(24).fill(""),
selected_mnemonic: null incorrect_word: null
}; };
export const mnemonic_word_added = data => { export const mnemonic_word_added = data => {
@ -18,13 +19,15 @@ export const mnemonicReducer = (state = { ...initial_state }, action) => {
var word = action.data.word; var word = action.data.word;
var id = action.data.id; var id = action.data.id;
var current_input = state.mnemonic_input; var current_input = state.mnemonic_input;
console.log(state.mnemonic_input);
current_input[id] = word; current_input[id] = word;
return { ...state, mnemonic_input: current_input }; return { ...state, mnemonic_input: current_input };
case "RESET_MNEMONIC": case "RESET_MNEMONIC":
return { ...initial_state }; return {
case "SELECT_MNEMONIC": mnemonic_input: new Array(24).fill(""),
return { ...state, selected_mnemonic: action.mnemonic }; incorrect_word: null
};
case "SET_INCORRECT_WORD":
return { ...state, incorrect_word: action.word };
default: default:
return state; return state;
} }

View File

@ -1,6 +1,7 @@
const initial_state = { const initial_state = {
plotting_in_proggress: false, plotting_in_proggress: false,
workspace_location: "", workspace_location: "",
t2: "",
final_location: "", final_location: "",
progress_location: "", progress_location: "",
progress: "", progress: "",

View File

@ -5,16 +5,17 @@ export const plotControl = () => ({
type: "PLOTTER_CONTROL" type: "PLOTTER_CONTROL"
}); });
export const startPlotting = (size, count, workspace, final) => { export const startPlotting = (k, n, t, t2, d, b) => {
var action = daemonMessage(); var action = daemonMessage();
action.message.command = "start_plotting"; action.message.command = "start_plotting";
action.message.data = { action.message.data = {
service: service_plotter, service: service_plotter,
k: size, k,
n: count, n,
t: workspace, t,
t2: workspace, t2,
d: final d,
b
}; };
return action; return action;
}; };

View File

@ -7,21 +7,37 @@ import isElectron from "is-electron";
import Box from "@material-ui/core/Box"; import Box from "@material-ui/core/Box";
import React from "react"; import React from "react";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import { TextField } from "@material-ui/core"; import List from "@material-ui/core/List";
import { useDispatch } from "react-redux"; 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 { makeStyles } from "@material-ui/core/styles";
import {
add_plot_directory_and_refresh,
remove_plot_directory_and_refresh
} from "../modules/message";
const styles = theme => ({ const styles = theme => ({
dialogTitle: { dialogTitle: {
width: 500 width: 500
}, },
addPlotButton: { addPlotButton: {
width: 80, width: 220,
marginLeft: theme.spacing(2), marginLeft: theme.spacing(2),
height: 56 height: 56
}, },
keyInput: { keyInput: {
marginTop: 10 marginTop: 10
},
dirList: {
width: "100%"
} }
}); });
@ -30,46 +46,40 @@ const useStyles = makeStyles(styles);
function AddPlotDialog(props) { function AddPlotDialog(props) {
const classes = useStyles(); const classes = useStyles();
const { onClose, open, ...other } = props; const { onClose, open, ...other } = props;
const [plotDir, setPlotDir] = React.useState("");
const dispatch = useDispatch(); const dispatch = useDispatch();
const handleEntering = () => {}; const directories = useSelector(
state => state.farming_state.harvester.plot_directories
);
const handleCancel = () => { const removePlotDir = dir => {
onClose({}); dispatch(remove_plot_directory_and_refresh(dir));
setPlotDir("");
}; };
const handleOk = () => { async function select() {
onClose(plotDir);
setPlotDir("");
};
async function select(setterFn) {
if (isElectron()) { if (isElectron()) {
const dialogOptions = { const dialogOptions = {
properties: ["openDirectory", "showHiddenFiles"], properties: ["openDirectory", "showHiddenFiles"],
buttonLabel: "Select Plot Directory" buttonLabel: "Select Plot Directory"
}; };
const result = await window.remote.dialog.showOpenDialog(dialogOptions); const result = await window.remote.dialog.showOpenDialog(dialogOptions);
const filePath = result["filePaths"][0]; console.log(result);
setterFn(filePath); if (!result.canceled) {
const filePath = result["filePaths"][0];
dispatch(add_plot_directory_and_refresh(filePath));
}
} else { } else {
dispatch( dispatch(
openDialog("", "This feature is available only from electron app") openDialog("", "This feature is available only from electron app")
); );
} }
} }
async function selectPlot() {
select(setPlotDir);
}
return ( return (
<Dialog <Dialog
disableBackdropClick disableBackdropClick
disableEscapeKeyDown disableEscapeKeyDown
maxWidth="md" maxWidth="md"
onEntering={handleEntering}
aria-labelledby="confirmation-dialog-title" aria-labelledby="confirmation-dialog-title"
open={open} open={open}
{...other} {...other}
@ -86,33 +96,44 @@ function AddPlotDialog(props) {
not created any plots, go to the plotting screen. not created any plots, go to the plotting screen.
</p> </p>
<Box display="flex"> <Box display="flex">
<Box flexGrow={1}> <List dense={true} className={classes.dirList}>
<TextField {directories.map(dir => (
disabled <ListItem>
className={classes.input} <ListItemAvatar>
fullWidth <Avatar>
label={plotDir === "" ? "Plot directory" : plotDir} <FolderIcon />
variant="outlined" </Avatar>
/> </ListItemAvatar>
</Box> <ListItemText primary={dir} />
<ListItemSecondaryAction>
<IconButton
edge="end"
aria-label="delete"
onClick={() => removePlotDir(dir)}
>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))}
</List>
</Box>
<Box display="flex">
<Box> <Box>
<Button <Button
onClick={selectPlot} onClick={select}
className={classes.addPlotButton} className={classes.addPlotButton}
variant="contained" variant="contained"
color="primary" color="primary"
> >
Select Add plot directory
</Button> </Button>
</Box> </Box>
</Box> </Box>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button autoFocus onClick={handleCancel} color="secondary"> <Button autoFocus onClick={onClose} color="secondary">
Cancel Close
</Button>
<Button onClick={handleOk} color="secondary">
Ok
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View File

@ -119,7 +119,7 @@ const Connections = props => {
<TableCell align="right"> <TableCell align="right">
{Math.floor(item.bytes_written / 1024)}/ {Math.floor(item.bytes_written / 1024)}/
{Math.floor(item.bytes_read / 1024)} KB {Math.floor(item.bytes_read / 1024)} KiB
</TableCell> </TableCell>
<TableCell align="right"> <TableCell align="right">
{service_connection_types[item.type]} {service_connection_types[item.type]}

View File

@ -22,12 +22,17 @@ import TableHead from "@material-ui/core/TableHead";
import DeleteForeverIcon from "@material-ui/icons/DeleteForever"; import DeleteForeverIcon from "@material-ui/icons/DeleteForever";
import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction";
import IconButton from "@material-ui/core/IconButton"; 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 { closeConnection, openConnection } from "../modules/farmerMessages";
import { import {
refreshPlots, refreshPlots,
deletePlot, deletePlot,
addPlotDirectory getPlotDirectories
} from "../modules/harvesterMessages"; } from "../modules/harvesterMessages";
import TablePagination from "@material-ui/core/TablePagination"; import TablePagination from "@material-ui/core/TablePagination";
@ -135,7 +140,7 @@ const getStatusItems = (
status_items.push({ status_items.push({
label: "Total size of local plots", 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: tooltip:
"You have " + "You have " +
(proportion * 100).toFixed(6) + (proportion * 100).toFixed(6) +
@ -305,7 +310,9 @@ const Plots = props => {
plots.sort((a, b) => b.size - a.size); plots.sort((a, b) => b.size - a.size);
const [page, setPage] = React.useState(0); const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10); 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) => { const handleChangePage = (event, newPage) => {
setPage(newPage); setPage(newPage);
@ -316,20 +323,21 @@ const Plots = props => {
setPage(0); setPage(0);
}; };
const deletePlotClick = filename => {
return () => {
dispatch(deletePlot(filename));
};
};
const refreshPlotsClick = () => { const refreshPlotsClick = () => {
dispatch(refreshPlots()); dispatch(refreshPlots());
}; };
const handleClose = response => { const addDirectoryHandleClose = () => {
setOpen(false); addDirectorySetOpen(false);
if (response) { };
dispatch(addPlotDirectory(response));
} const handleCloseDeletePlot = () => {
deletePlotSetOpen(false);
};
const handleCloseDeletePlotYes = () => {
handleCloseDeletePlot();
console.log("deleting plot", deletePlotName);
dispatch(deletePlot(deletePlotName));
}; };
return ( return (
@ -353,10 +361,11 @@ const Plots = props => {
color="primary" color="primary"
className={classes.addPlotButton} className={classes.addPlotButton}
onClick={() => { onClick={() => {
setOpen(true); dispatch(getPlotDirectories());
addDirectorySetOpen(true);
}} }}
> >
Add plots Manage plot directories
</Button> </Button>
<AddPlotDialog <AddPlotDialog
classes={{ classes={{
@ -364,8 +373,8 @@ const Plots = props => {
}} }}
id="ringtone-menu" id="ringtone-menu"
keepMounted keepMounted
open={open} open={addDirectoryOpen}
onClose={handleClose} onClose={addDirectoryHandleClose}
/> />
</Typography> </Typography>
</div> </div>
@ -401,7 +410,7 @@ const Plots = props => {
{Math.round( {Math.round(
(item.file_size * 1000) / (1024 * 1024 * 1024) (item.file_size * 1000) / (1024 * 1024 * 1024)
) / 1000} ) / 1000}
GB) GiB)
</TableCell> </TableCell>
<TableCell align="right"> <TableCell align="right">
<Tooltip title={item["plot-seed"]} interactive> <Tooltip title={item["plot-seed"]} interactive>
@ -424,7 +433,10 @@ const Plots = props => {
</TableCell> </TableCell>
<TableCell <TableCell
className={classes.clickable} className={classes.clickable}
onClick={deletePlotClick(item.filename)} onClick={() => {
deletePlotSetName(item.filename);
deletePlotSetOpen(true);
}}
align="right" align="right"
> >
<DeleteForeverIcon fontSize="small"></DeleteForeverIcon> <DeleteForeverIcon fontSize="small"></DeleteForeverIcon>
@ -463,7 +475,10 @@ const Plots = props => {
<IconButton <IconButton
edge="end" edge="end"
aria-label="delete" aria-label="delete"
onClick={deletePlotClick(filename)} onClick={() => {
deletePlotSetName(filename);
deletePlotSetOpen(true);
}}
> >
<DeleteForeverIcon /> <DeleteForeverIcon />
</IconButton> </IconButton>
@ -493,7 +508,10 @@ const Plots = props => {
<IconButton <IconButton
edge="end" edge="end"
aria-label="delete" aria-label="delete"
onClick={deletePlotClick(filename)} onClick={() => {
deletePlotSetName(filename);
deletePlotSetOpen(true);
}}
> >
<DeleteForeverIcon /> <DeleteForeverIcon />
</IconButton> </IconButton>
@ -507,6 +525,32 @@ const Plots = props => {
)} )}
</Grid> </Grid>
</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> </Paper>
); );
}; };

View File

@ -256,7 +256,7 @@ const getStatusItems = (state, connected) => {
status_items.push(min_item); status_items.push(min_item);
const space = 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 = { const space_item = {
label: "Estimated network space", label: "Estimated network space",
value: space, value: space,

View File

@ -1,8 +1,7 @@
import React, { Component, useEffect } from "react"; import React, { Component } from "react";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline"; import CssBaseline from "@material-ui/core/CssBaseline";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { withTheme } from "@material-ui/styles"; import { withTheme } from "@material-ui/styles";
import Container from "@material-ui/core/Container"; import Container from "@material-ui/core/Container";
import ArrowBackIosIcon from "@material-ui/icons/ArrowBackIos"; 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 { genereate_mnemonics, add_new_key_action } from "../modules/message";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import CssTextField from "../components/cssTextField"; import CssTextField from "../components/cssTextField";
import myStyle from "./style";
import { changeEntranceMenu, presentSelectKeys } from "../modules/entranceMenu"; 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 => { const MnemonicField = props => {
return ( return (
@ -48,17 +47,6 @@ const UIPart = props => {
words = []; 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() { function goBack() {
dispatch(changeEntranceMenu(presentSelectKeys)); dispatch(changeEntranceMenu(presentSelectKeys));
} }
@ -73,10 +61,15 @@ const UIPart = props => {
{" "} {" "}
</ArrowBackIosIcon> </ArrowBackIosIcon>
<div className={classes.grid_wrap}> <div className={classes.grid_wrap}>
<img className={classes.logo} src={logo} alt="Logo" />
<Container className={classes.grid} maxWidth="lg"> <Container className={classes.grid} maxWidth="lg">
<Typography className={classes.title} component="h4" variant="h4"> <h1 className={classes.titleSmallMargin}>New Wallet</h1>
New Wallet <p className={classes.whiteP}>
</Typography> 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}> <Grid container spacing={2}>
<Iterator mnemonic={words}></Iterator> <Iterator mnemonic={words}></Iterator>
</Grid> </Grid>

View File

@ -1,17 +1,15 @@
import React, { Component, useEffect } from "react"; import React, { Component } from "react";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline"; import CssBaseline from "@material-ui/core/CssBaseline";
import Link from "@material-ui/core/Link"; import Link from "@material-ui/core/Link";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { withTheme } from "@material-ui/styles"; import { withTheme } from "@material-ui/styles";
import Container from "@material-ui/core/Container"; import Container from "@material-ui/core/Container";
import ArrowBackIosIcon from "@material-ui/icons/ArrowBackIos"; import ArrowBackIosIcon from "@material-ui/icons/ArrowBackIos";
import { connect, useSelector } from "react-redux"; import { connect, useSelector } from "react-redux";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import CssTextField from "../components/cssTextField"; import CssTextField from "../components/cssTextField";
import myStyle from "./style"; import { useDispatch } from "react-redux";
import { useStore, useDispatch } from "react-redux";
import { mnemonic_word_added, resetMnemonic } from "../modules/mnemonic_input"; import { mnemonic_word_added, resetMnemonic } from "../modules/mnemonic_input";
import { unselectFingerprint } from "../modules/message"; import { unselectFingerprint } from "../modules/message";
import { import {
@ -19,7 +17,8 @@ import {
presentSelectKeys, presentSelectKeys,
presentRestoreBackup presentRestoreBackup
} from "../modules/entranceMenu"; } from "../modules/entranceMenu";
import { openDialog } from "../modules/dialogReducer"; import logo from "../assets/img/chia_logo.svg";
import myStyle from "./style";
const MnemonicField = props => { const MnemonicField = props => {
return ( return (
@ -32,8 +31,9 @@ const MnemonicField = props => {
color="primary" color="primary"
id={props.id} id={props.id}
label={props.index} label={props.index}
error={props.error}
autoFocus={props.autofocus} autoFocus={props.autofocus}
defaultValue="" defaultValue={props.value}
onChange={props.onChange} onChange={props.onChange}
/> />
</Grid> </Grid>
@ -41,12 +41,13 @@ const MnemonicField = props => {
}; };
const Iterator = props => { const Iterator = props => {
const store = useStore();
const dispatch = useDispatch(); const dispatch = useDispatch();
const mnemonic_state = useSelector(state => state.mnemonic_state);
const incorrect_word = useSelector(
state => state.mnemonic_state.incorrect_word
);
function handleTextFieldChange(e) { function handleTextFieldChange(e) {
console.log(e.target);
console.log(store);
var id = e.target.id + ""; var id = e.target.id + "";
var clean_id = id.replace("id_", ""); var clean_id = id.replace("id_", "");
var int_val = parseInt(clean_id) - 1; var int_val = parseInt(clean_id) - 1;
@ -60,6 +61,11 @@ const Iterator = props => {
<MnemonicField <MnemonicField
onChange={handleTextFieldChange} onChange={handleTextFieldChange}
key={i} 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} autofocus={focus}
id={"id_" + (i + 1)} id={"id_" + (i + 1)}
index={i + 1} index={i + 1}
@ -69,42 +75,44 @@ const Iterator = props => {
return indents; return indents;
}; };
const UIPart = props => { const UIPart = () => {
function goBack() { function goBack() {
dispatch(resetMnemonic()); dispatch(resetMnemonic());
dispatch(changeEntranceMenu(presentSelectKeys)); dispatch(changeEntranceMenu(presentSelectKeys));
} }
const dispatch = useDispatch(); const dispatch = useDispatch();
const [submitted, setSubmitted] = React.useState(false);
const mnemonic = useSelector(state => state.mnemonic_state.mnemonic_input);
const classes = myStyle();
function enterMnemonic() { function enterMnemonic() {
setSubmitted(true);
for (var i = 0; i < mnemonic.length; i++) {
if (mnemonic[i] === "") {
return;
}
}
dispatch(unselectFingerprint()); dispatch(unselectFingerprint());
dispatch(changeEntranceMenu(presentRestoreBackup)); 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 ( return (
<div className={classes.root}> <div className={classes.root}>
<Link onClick={goBack} href="#"> <Link onClick={goBack} href="#">
<ArrowBackIosIcon className={classes.navigator}> </ArrowBackIosIcon> <ArrowBackIosIcon className={classes.navigator}> </ArrowBackIosIcon>
</Link> </Link>
<div className={classes.grid_wrap}> <div className={classes.grid_wrap}>
<img className={classes.logo} src={logo} alt="Logo" />
<Container className={classes.grid} maxWidth="lg"> <Container className={classes.grid} maxWidth="lg">
<Typography className={classes.title} component="h4" variant="h4"> <h1 className={classes.titleSmallMargin}>
Import Wallet from Mnemonics 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}> <Grid container spacing={2}>
<Iterator mnemonic={words}></Iterator> <Iterator submitted={submitted}></Iterator>
</Grid> </Grid>
</Container> </Container>
</div> </div>
@ -134,10 +142,6 @@ class OldWallet extends Component {
this.classes = props.theme; this.classes = props.theme;
} }
componentDidMount(props) {
console.log("Input Mnemonic");
}
render() { render() {
return <UIPart props={this.props}></UIPart>; return <UIPart props={this.props}></UIPart>;
} }

View File

@ -14,7 +14,8 @@ import {
} from "@material-ui/core"; } from "@material-ui/core";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField"; 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 { openDialog } from "../modules/dialogReducer";
import isElectron from "is-electron"; import isElectron from "is-electron";
import { import {
@ -25,6 +26,8 @@ import {
} from "../modules/plotter_messages"; } from "../modules/plotter_messages";
import { stopService } from "../modules/daemon_messages"; import { stopService } from "../modules/daemon_messages";
import { service_plotter } from "../util/service_names"; import { service_plotter } from "../util/service_names";
import Input from "@material-ui/core/Input";
const drawerWidth = 180; const drawerWidth = 180;
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
@ -190,18 +193,18 @@ const useStyles = makeStyles(theme => ({
})); }));
const plot_size_options = [ const plot_size_options = [
// { label: "60MB", value: 16, workspace: "3.5GB" }, // { label: "60MiB", value: 16, workspace: "3.5GiB" },
{ label: "600MB", value: 25, workspace: "3.5GB" }, { label: "600MiB", value: 25, workspace: "3.5GiB" },
{ label: "1.3GB", value: 26, workspace: "7GB" }, { label: "1.3GiB", value: 26, workspace: "7GiB" },
{ label: "2.7GB", value: 27, workspace: "14.5GB" }, { label: "2.7GiB", value: 27, workspace: "14.5GiB" },
{ label: "5.6GB", value: 28, workspace: "30.3GB" }, { label: "5.6GiB", value: 28, workspace: "30.3GiB" },
{ label: "11.5GB", value: 29, workspace: "61GB" }, { label: "11.5GiB", value: 29, workspace: "61GiB" },
{ label: "23.8GB", value: 30, workspace: "128GB" }, { label: "23.8GiB", value: 30, workspace: "128GiB" },
{ label: "49.1GB", value: 31, workspace: "262GB" }, { label: "49.1GiB", value: 31, workspace: "262GiB" },
{ label: "101.4GB", value: 32, workspace: "566GB" }, { label: "101.4GiB", value: 32, workspace: "566GiB" },
{ label: "208.8GB", value: 33, workspace: "1095GB" }, { label: "208.8GiB", value: 33, workspace: "1095GiB" },
{ label: "429.8GB", value: 34, workspace: "2287GB" }, { label: "429.8GiB", value: 34, workspace: "2287GiB" },
{ label: "884.1GB", value: 35, workspace: "4672GB" } { label: "884.1GiB", value: 35, workspace: "4672GiB" }
]; ];
const WorkLocation = () => { const WorkLocation = () => {
@ -322,11 +325,13 @@ const CreatePlot = () => {
const work_location = useSelector( const work_location = useSelector(
state => state.plot_control.workspace_location state => state.plot_control.workspace_location
); );
let t2 = useSelector(state => state.plot_control.t2);
const final_location = useSelector( const final_location = useSelector(
state => state.plot_control.final_location state => state.plot_control.final_location
); );
const [plotSize, setPlotSize] = React.useState(25); const [plotSize, setPlotSize] = React.useState(25);
const [plotCount, setPlotCount] = React.useState(1); const [plotCount, setPlotCount] = React.useState(1);
const [maxRam, setMaxRam] = React.useState(2000);
const changePlotSize = event => { const changePlotSize = event => {
setPlotSize(event.target.value); setPlotSize(event.target.value);
@ -334,6 +339,9 @@ const CreatePlot = () => {
const changePlotCount = event => { const changePlotCount = event => {
setPlotCount(event.target.value); setPlotCount(event.target.value);
}; };
const handleSetMaxRam = event => {
setMaxRam(event.target.value);
};
function create() { function create() {
if (!work_location || !final_location) { if (!work_location || !final_location) {
@ -342,7 +350,10 @@ const CreatePlot = () => {
} }
const N = plotCount; const N = plotCount;
const K = plotSize; 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 = []; var plot_count_options = [];
@ -374,7 +385,7 @@ const CreatePlot = () => {
<Grid item xs={12}> <Grid item xs={12}>
<div className={classes.cardSubSection}> <div className={classes.cardSubSection}>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={6}> <Grid item xs={4}>
<FormControl <FormControl
fullWidth fullWidth
variant="outlined" variant="outlined"
@ -398,7 +409,7 @@ const CreatePlot = () => {
</Select> </Select>
</FormControl> </FormControl>
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={4}>
<FormControl <FormControl
fullWidth fullWidth
variant="outlined" variant="outlined"
@ -418,6 +429,27 @@ const CreatePlot = () => {
</Select> </Select>
</FormControl> </FormControl>
</Grid> </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> </Grid>
</div> </div>
</Grid> </Grid>

View File

@ -191,7 +191,7 @@ const SelectKey = () => {
<div className={classes.paper}> <div className={classes.paper}>
<img className={classes.logo} src={logo} alt="Logo" /> <img className={classes.logo} src={logo} alt="Logo" />
{public_key_fingerprints && public_key_fingerprints.length > 0 ? ( {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}> <span className={classes.centeredSpan}>
<h2 className={classes.whiteText}>Sign In</h2> <h2 className={classes.whiteText}>Sign In</h2>

View File

@ -26,8 +26,11 @@ const UIPart = props => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const classes = myStyle(); const classes = myStyle();
if (words.length === 0) {
words = null; for (let word of words) {
if (word === "") {
words = null;
}
} }
function goBack() { function goBack() {
@ -60,10 +63,8 @@ const UIPart = props => {
const file_path = e.dataTransfer.files[0].path; const file_path = e.dataTransfer.files[0].path;
if (fingerprint !== null) { if (fingerprint !== null) {
debugger;
dispatch(log_in_and_import_backup(fingerprint, file_path)); dispatch(log_in_and_import_backup(fingerprint, file_path));
} else if (words !== null) { } else if (words !== null) {
debugger;
dispatch(add_and_restore_from_backup(words, file_path)); dispatch(add_and_restore_from_backup(words, file_path));
} }
}; };

View File

@ -28,7 +28,8 @@ const myStyle = makeStyles(theme => ({
}, },
grid_wrap: { grid_wrap: {
paddingLeft: theme.spacing(10), paddingLeft: theme.spacing(10),
paddingRight: theme.spacing(10) paddingRight: theme.spacing(10),
textAlign: "center"
}, },
grid: { grid: {
display: "flex", display: "flex",
@ -50,6 +51,11 @@ const myStyle = makeStyles(theme => ({
marginTop: theme.spacing(4), marginTop: theme.spacing(4),
marginBottom: theme.spacing(8) marginBottom: theme.spacing(8)
}, },
titleSmallMargin: {
color: "#ffffff",
marginTop: theme.spacing(4),
marginBottom: theme.spacing(2)
},
navigator: { navigator: {
color: "#ffffff", color: "#ffffff",
marginTop: theme.spacing(4), marginTop: theme.spacing(4),
@ -84,6 +90,14 @@ const myStyle = makeStyles(theme => ({
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "center" justifyContent: "center"
},
logo: {
marginTop: theme.spacing(0),
marginBottom: theme.spacing(1)
},
whiteP: {
color: "white",
fontSize: "18px"
} }
})); }));

View File

@ -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_full_node = "chia_full_node";
export const service_farmer = "chia_farmer"; export const service_farmer = "chia_farmer";
export const service_harvester = "chia_harvester"; export const service_harvester = "chia_harvester";

View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
echo "Sorry, this script is broken."
echo "You must manually remove files from ~/.chia/*/db/ for now."

View File

@ -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

View File

@ -1,3 +0,0 @@
# Stops all python servers and VDF processes running on this machine
. _chia-common

View File

@ -3,10 +3,10 @@ from setuptools import setup
dependencies = [ dependencies = [
"aiter==0.13.20191203", # Used for async generator tools "aiter==0.13.20191203", # Used for async generator tools
"blspy==0.2.0", # Signature library "blspy==0.2.1", # Signature library
"chiavdf==0.12.22", # timelord and vdf verification "chiavdf==0.12.23", # timelord and vdf verification
"chiabip158==0.16", # bip158-style wallet filters "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==0.4", # contract language
"clvm-tools==0.1.1", # clvm compiler tools "clvm-tools==0.1.1", # clvm compiler tools
"aiohttp==3.6.2", # HTTP server for full node rpc "aiohttp==3.6.2", # HTTP server for full node rpc
@ -69,18 +69,10 @@ kwargs = dict(
"src.wallet.trading", "src.wallet.trading",
"src.ssl", "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={ entry_points={
"console_scripts": [ "console_scripts": [
"chia = src.cmds.chia:main", "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_full_node = src.server.start_full_node:main",
"chia_harvester = src.server.start_harvester:main", "chia_harvester = src.server.start_harvester:main",
"chia_farmer = src.server.start_farmer:main", "chia_farmer = src.server.start_farmer:main",

View File

@ -1,4 +1,8 @@
from pathlib import Path from pathlib import Path
from typing import List
from blspy import AugSchemeMPL, G1Element, G2Element
from src.cmds.init import check_keys from src.cmds.init import check_keys
from src.util.keychain import ( from src.util.keychain import (
generate_mnemonic, generate_mnemonic,
@ -20,6 +24,8 @@ command_list = [
"add", "add",
"delete", "delete",
"delete_all", "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)" "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 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): def make_parser(parser):
@ -54,7 +66,31 @@ def make_parser(parser):
"--fingerprint", "--fingerprint",
type=int, type=int,
default=None, 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( parser.add_argument(
@ -76,9 +112,8 @@ def generate_and_print():
""" """
mnemonic = generate_mnemonic() mnemonic = generate_mnemonic()
mnemonics_string = mnemonic_to_string(mnemonic) print("Generating private key. Mnemonic (24 secret words):")
print("Generating private key. Mnemonic:") print(mnemonic)
print(mnemonics_string)
print( print(
"Note that this key has not been added to the keychain. Run chia keys add_seed -m [MNEMONICS] to add" "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( print(
f"Added private key with public key fingerprint {fingerprint} and mnemonic" f"Added private key with public key fingerprint {fingerprint} and mnemonic"
) )
print(f"{mnemonic_to_string(mnemonic)}") print(mnemonic)
except ValueError as e: except ValueError as e:
print(e) print(e)
return 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(): def show_all_keys():
""" """
Prints all keys and mnemonics (if available). Prints all keys and mnemonics (if available).
@ -163,9 +181,8 @@ def show_all_keys():
) )
assert seed is not None assert seed is not None
mnemonic = bytes_to_mnemonic(seed) mnemonic = bytes_to_mnemonic(seed)
mnemonic_string = mnemonic_to_string(mnemonic) print(" Mnemonic seed (24 secret words):")
print(" Mnemonic seed:") print(mnemonic)
print(mnemonic_string)
def delete(args): def delete(args):
@ -182,6 +199,55 @@ def delete(args):
keychain.delete_key_by_fingerprint(fingerprint) 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): def handler(args, parser):
if args.command is None or len(args.command) < 1: if args.command is None or len(args.command) < 1:
help_message() help_message()
@ -213,3 +279,7 @@ def handler(args, parser):
keychain.delete_all_keys() keychain.delete_all_keys()
if command == "generate_and_print": if command == "generate_and_print":
generate_and_print() generate_and_print()
if command == "sign":
sign(args)
if command == "verify":
verify(args)

View File

@ -101,7 +101,7 @@ async def netstorge_async(args, parser):
network_space_terrabytes_estimate = network_space_bytes_estimate / 1024 ** 4 network_space_terrabytes_estimate = network_space_bytes_estimate / 1024 ** 4
print( print(
f"The elapsed time between blocks is reported as {time_delta}.\n" 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: except Exception as e:

View File

@ -1,6 +1,10 @@
from pathlib import Path from pathlib import Path
import logging 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.create_plots import create_plots
from src.plotting.check_plots import check_plots from src.plotting.check_plots import check_plots
from src.util.logging import initialize_logging from src.util.logging import initialize_logging
@ -9,11 +13,7 @@ from src.util.logging import initialize_logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
command_list = [ command_list = ["create", "check", "add", "remove", "show"]
"create",
"check",
"add",
]
def help_message(): def help_message():
@ -21,12 +21,14 @@ def help_message():
print(f"command can be any of {command_list}") print(f"command can be any of {command_list}")
print("") print("")
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)" + " -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("-s [sk_seed] -i [index] are available for debugging")
print("chia plots check -n [num checks] (checks plots)") print("chia plots check -n [num checks] (checks plots)")
print("chia plots add -d [directory] (adds a directory of 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): def make_parser(parser):
@ -35,10 +37,7 @@ def make_parser(parser):
"-n", "--num", help="Number of plots or challenges", type=int, default=None "-n", "--num", help="Number of plots or challenges", type=int, default=None
) )
parser.add_argument( parser.add_argument(
"-i", "--index", help="First plot index", type=int, default=None "-b", "--buffer", help="Mebibytes for sort/plot buffer", type=int, default=2000
)
parser.add_argument(
"-b", "--buffer", help="Megabytes for sort/plot buffer", type=int, default=2048
) )
parser.add_argument( parser.add_argument(
"-f", "-f",
@ -50,9 +49,6 @@ def make_parser(parser):
parser.add_argument( parser.add_argument(
"-p", "--pool_public_key", help="Hex public key of pool", type=str, default=None "-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( parser.add_argument(
"-t", "-t",
"--tmp_dir", "--tmp_dir",
@ -85,6 +81,19 @@ def make_parser(parser):
parser.print_help = lambda self=parser: help_message() 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): def handler(args, parser):
if args.command is None or len(args.command) < 1: if args.command is None or len(args.command) < 1:
help_message() help_message()
@ -109,3 +118,8 @@ def handler(args, parser):
elif command == "add": elif command == "add":
str_path = args.final_dir str_path = args.final_dir
add_plot_directory(str_path, root_path) 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)

View File

@ -3,15 +3,17 @@ import asyncio
import time import time
from time import struct_time, localtime 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.server.connection import NodeType
from src.types.header_block import HeaderBlock from src.types.header_block import HeaderBlock
from src.rpc.full_node_rpc_client import FullNodeRpcClient 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.byte_types import hexstr_to_bytes
from src.util.config import str2bool from src.util.config import str2bool
from src.util.config import load_config from src.util.config import load_config
from src.util.default_root import DEFAULT_ROOT_PATH from src.util.default_root import DEFAULT_ROOT_PATH
from src.cmds.units import units
def make_parser(parser): def make_parser(parser):
@ -86,6 +88,26 @@ def make_parser(parser):
type=int, type=int,
default=8555, 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) parser.set_defaults(function=show)
@ -178,7 +200,7 @@ async def show_async(args, parser):
print("Connections") print("Connections")
print( print(
"Type IP Ports NodeID Last Connect" "Type IP Ports NodeID Last Connect"
+ " MB Up|Dwn" + " MiB Up|Dwn"
) )
for con in connections: for con in connections:
last_connect_tuple = struct_time(localtime(con["last_message_time"])) 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: if block.header.data.aggregated_signature is None:
aggregated_signature = block.header.data.aggregated_signature aggregated_signature = block.header.data.aggregated_signature
else: else:
aggregated_signature = block.header.data.aggregated_signature.sig aggregated_signature = block.header.data.aggregated_signature
print("Block", block.header.data.height, ":") print("Block", block.header.data.height, ":")
print( print(
f"Header Hash 0x{args.block_by_header_hash}\n" f"Header Hash 0x{args.block_by_header_hash}\n"
@ -289,6 +311,55 @@ async def show_async(args, parser):
else: else:
print("Block with header hash", args.block_by_header_hash, "not found.") 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: except Exception as e:
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError): if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
print(f"Connection error. Check if full node is running at {args.rpc_port}") print(f"Connection error. Check if full node is running at {args.rpc_port}")

9
src/cmds/units.py Normal file
View 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
}

View File

@ -99,7 +99,7 @@ testnet_kwargs = {
# Coinbase rewards are not spendable for 200 blocks # Coinbase rewards are not spendable for 200 blocks
"COINBASE_FREEZE_PERIOD": 200, "COINBASE_FREEZE_PERIOD": 200,
# Max coin amount uint(1 << 64) # Max coin amount uint(1 << 64)
"MAX_COIN_AMOUNT": 0xffffffffffffffff, "MAX_COIN_AMOUNT": 0xFFFFFFFFFFFFFFFF,
# Raw size per block target = 1,000,000 bytes # Raw size per block target = 1,000,000 bytes
# Rax TX (single in, single out) = 219 bytes (not compressed) # Rax TX (single in, single out) = 219 bytes (not compressed)
# TX = 457 vBytes # TX = 457 vBytes

View File

@ -35,7 +35,7 @@ log = logging.getLogger(__name__)
if getattr(sys, "frozen", False): if getattr(sys, "frozen", False):
name_map = { name_map = {
"chia": "chia", "chia": "chia",
"chia-wallet": "wallet_server", "chia_wallet": "start_wallet",
"chia_full_node": "start_full_node", "chia_full_node": "start_full_node",
"chia_harvester": "start_harvester", "chia_harvester": "start_harvester",
"chia_farmer": "start_farmer", "chia_farmer": "start_farmer",
@ -212,6 +212,7 @@ class WebSocketServer:
t = request["t"] t = request["t"]
t2 = request["t2"] t2 = request["t2"]
d = request["d"] d = request["d"]
b = request["b"]
command_args: List[str] = [] command_args: List[str] = []
command_args += service_name.split(" ") command_args += service_name.split(" ")
@ -220,6 +221,7 @@ class WebSocketServer:
command_args.append(f"-t={t}") command_args.append(f"-t={t}")
command_args.append(f"-2={t2}") command_args.append(f"-2={t2}")
command_args.append(f"-d={d}") command_args.append(f"-d={d}")
command_args.append(f"-b={b}")
error = None error = None
success = False success = False

View File

@ -221,7 +221,9 @@ class Blockchain:
prev_challenge_hash = block.proof_of_space.challenge_hash prev_challenge_hash = block.proof_of_space.challenge_hash
new_difficulty: Optional[uint64] 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( new_difficulty = get_next_difficulty(
self.constants, self.headers, self.height_to_hash, block.header 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 # This coin is not in the current heaviest chain, so it must be in the fork
if rem not in additions_since_fork: if rem not in additions_since_fork:
# This coin does not exist in the fork # This coin does not exist in the fork
# TODO: fix this, there is a consensus bug here
return Err.UNKNOWN_UNSPENT return Err.UNKNOWN_UNSPENT
if rem in coinbases_since_fork: if rem in coinbases_since_fork:
# This coin is a coinbase coin # This coin is a coinbase coin

View File

@ -128,9 +128,7 @@ def get_next_difficulty(
) )
# Take only DIFFICULTY_SIGNIFICANT_BITS significant bits # Take only DIFFICULTY_SIGNIFICANT_BITS significant bits
new_difficulty = uint64( new_difficulty = uint64(
truncate_to_significant_bits( truncate_to_significant_bits(new_difficulty_precise, constants.SIGNIFICANT_BITS)
new_difficulty_precise, constants.SIGNIFICANT_BITS
)
) )
assert count_significant_bits(new_difficulty) <= constants.SIGNIFICANT_BITS assert count_significant_bits(new_difficulty) <= constants.SIGNIFICANT_BITS

View File

@ -1722,7 +1722,11 @@ class FullNode:
) -> OutboundMessageGenerator: ) -> OutboundMessageGenerator:
if self.global_connections is None: if self.global_connections is None:
return 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( yield OutboundMessage(
NodeType.FULL_NODE, NodeType.FULL_NODE,

View File

@ -13,10 +13,15 @@ from src.protocols import harvester_protocol
from src.server.connection import PeerConnections from src.server.connection import PeerConnections
from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage from src.server.outbound_message import Delivery, Message, NodeType, OutboundMessage
from src.types.proof_of_space import ProofOfSpace 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.api_decorators import api_request
from src.util.ints import uint8 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__) log = logging.getLogger(__name__)
@ -24,7 +29,7 @@ log = logging.getLogger(__name__)
class Harvester: class Harvester:
config: Dict config: Dict
provers: Dict[Path, PlotInfo] provers: Dict[Path, PlotInfo]
failed_to_open_filenames: Set[Path] failed_to_open_filenames: Dict[Path, int]
no_key_filenames: Set[Path] no_key_filenames: Set[Path]
farmer_public_keys: List[G1Element] farmer_public_keys: List[G1Element]
pool_public_keys: List[G1Element] pool_public_keys: List[G1Element]
@ -41,7 +46,7 @@ class Harvester:
# From filename to prover # From filename to prover
self.provers = {} self.provers = {}
self.failed_to_open_filenames = set() self.failed_to_open_filenames = {}
self.no_key_filenames = set() self.no_key_filenames = set()
self._is_shutdown = False self._is_shutdown = False
@ -93,24 +98,28 @@ class Harvester:
return ( return (
response_plots, 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], [str(s) for s in self.no_key_filenames],
) )
async def _refresh_plots(self): async def _refresh_plots(self):
locked: bool = self._refresh_lock.locked()
changed: bool = False
async with self._refresh_lock: async with self._refresh_lock:
( if not locked:
changed, # Avoid double refreshing of plots
self.provers, (
self.failed_to_open_filenames, changed,
self.no_key_filenames, self.provers,
) = load_plots( self.failed_to_open_filenames,
self.provers, self.no_key_filenames,
self.failed_to_open_filenames, ) = load_plots(
self.farmer_public_keys, self.provers,
self.pool_public_keys, self.failed_to_open_filenames,
self.root_path, self.farmer_public_keys,
) self.pool_public_keys,
self.root_path,
)
if changed: if changed:
self._state_changed("plots") self._state_changed("plots")
@ -127,13 +136,15 @@ class Harvester:
return True return True
async def _add_plot_directory(self, str_path: str) -> bool: async def _add_plot_directory(self, str_path: str) -> bool:
config = load_config(self.root_path, "config.yaml") add_plot_directory(str_path, self.root_path)
if str(Path(str_path).resolve()) not in config["harvester"]["plot_directories"]: await self._refresh_plots()
config["harvester"]["plot_directories"].append( return True
str(Path(str_path).resolve())
) async def _get_plot_directories(self) -> List[str]:
save_config(self.root_path, "config.yaml", config) return get_plot_directories(self.root_path)
await self._refresh_plots()
async def _remove_plot_directory(self, str_path: str) -> bool:
remove_plot_directory(str_path, self.root_path)
return True return True
def _set_global_connections(self, global_connections: Optional[PeerConnections]): def _set_global_connections(self, global_connections: Optional[PeerConnections]):
@ -195,14 +206,14 @@ class Harvester:
quality_strings = prover.get_qualities_for_challenge( quality_strings = prover.get_qualities_for_challenge(
new_challenge.challenge_hash new_challenge.challenge_hash
) )
except RuntimeError: except Exception:
log.error("Error using prover object. Reinitializing prover object.") log.error("Error using prover object. Reinitializing prover object.")
try: try:
self.prover = DiskProver(str(filename)) self.prover = DiskProver(str(filename))
quality_strings = self.prover.get_qualities_for_challenge( quality_strings = self.prover.get_qualities_for_challenge(
new_challenge.challenge_hash new_challenge.challenge_hash
) )
except RuntimeError: except Exception:
log.error( log.error(
f"Retry-Error using prover object on {filename}. Giving up." f"Retry-Error using prover object on {filename}. Giving up."
) )
@ -231,7 +242,7 @@ class Harvester:
awaitables = [] awaitables = []
for filename, plot_info in self.provers.items(): 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(), plot_info.prover.get_id(),
new_challenge.challenge_hash, new_challenge.challenge_hash,
self.constants.NUMBER_ZERO_BITS_CHALLENGE_SIG, self.constants.NUMBER_ZERO_BITS_CHALLENGE_SIG,

View File

@ -28,7 +28,7 @@ def check_plots(args, root_path):
for pk in config["farmer"]["pool_public_keys"] for pk in config["farmer"]["pool_public_keys"]
] ]
_, provers, failed_to_open_filenames, no_key_filenames = load_plots( _, 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: if len(provers) > 0:
log.info("") log.info("")
@ -78,7 +78,7 @@ def check_plots(args, root_path):
log.info("Summary") log.info("Summary")
total_plots: int = sum(list(total_good_plots.values())) total_plots: int = sum(list(total_good_plots.values()))
log.info( 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()): for (k, count) in sorted(dict(total_good_plots).items()):
log.info(f"{count} plots of size {k}") log.info(f"{count} plots of size {k}")

View File

@ -3,6 +3,7 @@ from pathlib import Path
from blspy import PrivateKey, G1Element from blspy import PrivateKey, G1Element
from chiapos import DiskProver from chiapos import DiskProver
from dataclasses import dataclass from dataclasses import dataclass
import time
import logging import logging
import traceback import traceback
from src.types.proof_of_space import ProofOfSpace from src.types.proof_of_space import ProofOfSpace
@ -70,7 +71,7 @@ def stream_plot_info(
return data 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") config = load_config(root_path, "config.yaml")
if str(Path(str_path).resolve()) not in config["harvester"]["plot_directories"]: if str(Path(str_path).resolve()) not in config["harvester"]["plot_directories"]:
config["harvester"]["plot_directories"].append(str(Path(str_path).resolve())) config["harvester"]["plot_directories"].append(str(Path(str_path).resolve()))
@ -78,14 +79,38 @@ def add_plot_directory(str_path, root_path):
return config 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( def load_plots(
provers: Dict[Path, PlotInfo], provers: Dict[Path, PlotInfo],
failed_to_open_filenames: Set[Path], failed_to_open_filenames: Dict[Path, int],
farmer_public_keys: Optional[List[G1Element]], farmer_public_keys: Optional[List[G1Element]],
pool_public_keys: Optional[List[G1Element]], pool_public_keys: Optional[List[G1Element]],
root_path: Path, root_path: Path,
open_no_key_filenames=False, 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") config_file = load_config(root_path, "config.yaml", "harvester")
changed = False changed = False
no_key_filenames: Set[Path] = set() no_key_filenames: Set[Path] = set()
@ -96,16 +121,22 @@ def load_plots(
for paths in plot_filenames.values(): for paths in plot_filenames.values():
all_filenames += paths all_filenames += paths
total_size = 0 total_size = 0
new_provers: Dict[Path, PlotInfo] = {}
for filename in all_filenames: 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.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: try:
prover = DiskProver(str(filename)) prover = DiskProver(str(filename))
( (
@ -141,7 +172,7 @@ def load_plots(
plot_public_key: G1Element = ProofOfSpace.generate_plot_public_key( plot_public_key: G1Element = ProofOfSpace.generate_plot_public_key(
local_sk.get_g1(), farmer_public_key local_sk.get_g1(), farmer_public_key
) )
provers[filename] = PlotInfo( new_provers[filename] = PlotInfo(
prover, prover,
pool_public_key, pool_public_key,
farmer_public_key, farmer_public_key,
@ -155,13 +186,13 @@ def load_plots(
except Exception as e: except Exception as e:
tb = traceback.format_exc() tb = traceback.format_exc()
log.error(f"Failed to open file {filename}. {e} {tb}") 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 continue
log.info( 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( 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)

View File

@ -15,6 +15,8 @@ class HarvesterRpcApi:
"/refresh_plots": self.refresh_plots, "/refresh_plots": self.refresh_plots,
"/delete_plot": self.delete_plot, "/delete_plot": self.delete_plot,
"/add_plot_directory": self.add_plot_directory, "/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]: async def _state_changed(self, change: str) -> List[str]:
@ -46,3 +48,12 @@ class HarvesterRpcApi:
dirname = request["dirname"] dirname = request["dirname"]
success = await self.service._add_plot_directory(dirname) success = await self.service._add_plot_directory(dirname)
return {"success": success} 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}

View File

@ -21,4 +21,12 @@ class HarvesterRpcClient(RpcClient):
return await self.fetch("delete_plot", {"filename": filename}) return await self.fetch("delete_plot", {"filename": filename})
async def add_plot_directory(self, dirname: str) -> bool: 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"
]

View File

@ -155,7 +155,7 @@ class RpcServer:
self.log.info(f"Rpc call <- {message['command']}") self.log.info(f"Rpc call <- {message['command']}")
response = await self.ws_api(message) response = await self.ws_api(message)
if response is not None: 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)) await websocket.send_str(format_response(message, response))
except Exception as e: except Exception as e:

View File

@ -35,7 +35,7 @@ log = logging.getLogger(__name__)
class WalletRpcApi: class WalletRpcApi:
def __init__(self, wallet_node: WalletNode): def __init__(self, wallet_node: WalletNode):
self.service = wallet_node self.service = wallet_node
self.service_name = "chia-wallet" self.service_name = "chia_wallet"
def get_routes(self) -> Dict[str, Callable]: def get_routes(self) -> Dict[str, Callable]:
return { return {
@ -137,7 +137,7 @@ class WalletRpcApi:
} }
if wallet_id is not None: if wallet_id is not None:
data["wallet_id"] = wallet_id 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: async def get_next_puzzle_hash(self, request: Dict) -> Dict:
""" """
@ -574,7 +574,22 @@ class WalletRpcApi:
# Adding a key from 24 word mnemonic # Adding a key from 24 word mnemonic
mnemonic = request["mnemonic"] mnemonic = request["mnemonic"]
passphrase = "" 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: else:
return {"success": False} return {"success": False}
@ -609,7 +624,8 @@ class WalletRpcApi:
fingerprint = request["fingerprint"] fingerprint = request["fingerprint"]
self.service.keychain.delete_key_by_fingerprint(fingerprint) self.service.keychain.delete_key_by_fingerprint(fingerprint)
path = path_from_root( 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(): if path.exists():
path.unlink() path.unlink()

View 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})

View File

@ -23,6 +23,9 @@ from src.util.ints import uint16
import traceback import traceback
log = logging.getLogger(__name__)
async def initialize_pipeline( async def initialize_pipeline(
srwt_aiter, srwt_aiter,
api: Any, api: Any,
@ -161,8 +164,11 @@ async def connection_to_outbound(
""" """
connection, global_connections = pair connection, global_connections = pair
if connection.on_connect: if connection.on_connect:
async for outbound_message in connection.on_connect(): try:
yield connection, outbound_message, global_connections 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( 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. # Only yield a connection if the handshake is succesful and the connection is not a duplicate.
yield connection, global_connections 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.") 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 # Make sure to close the connection even if it's not in global connections
connection.close() connection.close()

View File

@ -79,6 +79,9 @@ class ChiaServer:
# Aiter used to broadcase messages # Aiter used to broadcase messages
self._outbound_aiter: push_aiter = push_aiter() 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 # Taks list to keep references to tasks, so they don'y get GCd
self._tasks: List[asyncio.Task] = [] self._tasks: List[asyncio.Task] = []
if local_type != NodeType.INTRODUCER: if local_type != NodeType.INTRODUCER:
@ -130,9 +133,16 @@ class ChiaServer:
ssl_context = ssl_context_for_client(self.root_path, self.config, auth=auth) ssl_context = ssl_context_for_client(self.root_path, self.config, auth=auth)
try: try:
reader, writer = await asyncio.open_connection( # Sometimes open_connection takes a long time, so we add it as a task, and cancel
target_node.host, int(target_node.port), ssl=ssl_context # 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: except Exception as e:
self.log.warning( self.log.warning(
f"Could not connect to {target_node}. {type(e)}{str(e)}. Aborting and removing peer." 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() self._outbound_aiter.stop()
if not self._srwt_aiter.is_stopped(): if not self._srwt_aiter.is_stopped():
self._srwt_aiter.stop() self._srwt_aiter.stop()
for task in self._oc_tasks:
task.cancel()
def _initialize_ping_task(self): def _initialize_ping_task(self):
async def ping(): async def ping():

View File

@ -90,6 +90,7 @@ class Service:
assert network_id is not None assert network_id is not None
self._node_type = node_type self._node_type = node_type
self._service_name = service_name
proctitle_name = f"chia_{service_name}" proctitle_name = f"chia_{service_name}"
setproctitle(proctitle_name) setproctitle(proctitle_name)
@ -186,41 +187,51 @@ class Service:
except NotImplementedError: except NotImplementedError:
self._log.info("signal handlers unsupported") 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()) self._task = asyncio.create_task(_run())
async def run(self): async def run(self):
self.start() self.start()
await self.wait_closed() await self.wait_closed()
self._log.info("Closed all node servers.")
return 0 return 0
def stop(self): def stop(self):
if not self._is_stopping: if not self._is_stopping:
self._is_stopping = True 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: for _ in self._server_sockets:
_.close() _.close()
self._log.info("Cancelling reconnect task")
for _ in self._reconnect_tasks: for _ in self._reconnect_tasks:
_.cancel() _.cancel()
self._log.info("Closing connections")
self._server.close_all() self._server.close_all()
self._api._shut_down = True self._api._shut_down = True
self._log.info("Stopping introducer task")
if self._introducer_poll_task: if self._introducer_poll_task:
self._introducer_poll_task.cancel() self._introducer_poll_task.cancel()
async def wait_closed(self): 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: if self._rpc_task:
self._log.info("Waiting for RPC server")
await (await self._rpc_task)() await (await self._rpc_task)()
self._log.info("Closed RPC server.") 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): async def async_run_service(*args, **kwargs):

View File

@ -15,7 +15,7 @@ test_constants = make_test_constants_without_genesis(
"NUMBER_ZERO_BITS_CHALLENGE_SIG": 1, "NUMBER_ZERO_BITS_CHALLENGE_SIG": 1,
"CLVM_COST_RATIO_CONSTANT": 108, "CLVM_COST_RATIO_CONSTANT": 108,
"COINBASE_FREEZE_PERIOD": 0, "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
} }
) )

View File

@ -94,12 +94,10 @@ class BlockTools:
temp_dir = plot_dir / "tmp" temp_dir = plot_dir / "tmp"
mkdir(temp_dir) mkdir(temp_dir)
args = Namespace() args = Namespace()
args.sk_seed = std_hash(b"").hex()
# Can't go much lower than 18, since plots start having no solutions # Can't go much lower than 18, since plots start having no solutions
args.size = 18 args.size = 18
# Uses many plots for testing, in order to guarantee proofs of space at every height # Uses many plots for testing, in order to guarantee proofs of space at every height
args.num = 40 args.num = 40
args.index = 0
args.buffer = 32 args.buffer = 32
args.farmer_public_key = bytes(self.farmer_pk).hex() args.farmer_public_key = bytes(self.farmer_pk).hex()
args.pool_public_key = bytes(self.pool_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: if len(self.pool_pubkeys) == 0 or len(farmer_pubkeys) == 0:
raise RuntimeError("Keys not generated. Run `chia generate keys`") raise RuntimeError("Keys not generated. Run `chia generate keys`")
_, self.plots, _, _ = load_plots( _, self.plots, _, _ = load_plots(
{}, set(), farmer_pubkeys, self.pool_pubkeys, root_path {}, {}, farmer_pubkeys, self.pool_pubkeys, root_path
) )
def get_plot_signature( def get_plot_signature(
@ -252,8 +250,7 @@ class BlockTools:
else: else:
block1 = block_list[0] block1 = block_list[0]
timestamp1 = uint64( timestamp1 = uint64(
block1.header.data.timestamp block1.header.data.timestamp - test_constants.BLOCK_TIME_TARGET
- test_constants.BLOCK_TIME_TARGET
) )
iters1 = uint64(0) iters1 = uint64(0)
timestamp2 = block_list[height2].header.data.timestamp timestamp2 = block_list[height2].header.data.timestamp

View File

@ -1,10 +1,10 @@
SERVICES_FOR_GROUP = { 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(), "node": "chia_full_node".split(),
"harvester": "chia_harvester".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(), "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(), "introducer": "chia_introducer".split(),
"simulator": "chia_full_node_simulator".split(), "simulator": "chia_full_node_simulator".split(),
} }

View File

@ -146,6 +146,7 @@ class WalletNode:
self.root_path, f"{self.config['database_path']}-{db_path_key_suffix}" self.root_path, f"{self.config['database_path']}-{db_path_key_suffix}"
) )
mkdir(path.parent) mkdir(path.parent)
self.wallet_state_manager = await WalletStateManager.create( self.wallet_state_manager = await WalletStateManager.create(
private_key, self.config, path, self.constants private_key, self.config, path, self.constants
) )
@ -186,7 +187,8 @@ class WalletNode:
async def _await_closed(self): async def _await_closed(self):
if self.sync_generator_task is not None: 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: if self.wallet_state_manager is None or self.backup_initialized is False:
return return
await self.wsm_close_task await self.wsm_close_task
@ -242,7 +244,11 @@ class WalletNode:
self.server.push_message(msg) self.server.push_message(msg)
async def _messages_to_resend(self) -> List[OutboundMessage]: 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 [] return []
messages: List[OutboundMessage] = [] messages: List[OutboundMessage] = []

View File

@ -709,7 +709,10 @@ class WalletStateManager:
if header_block is not None: if header_block is not None:
if not await self.validate_header_block(block, header_block): if not await self.validate_header_block(block, header_block):
return ReceiveBlockResult.INVALID_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 assert header_block.challenge.new_work_difficulty is not None
self.difficulty_resets_prev[ self.difficulty_resets_prev[
block.header_hash block.header_hash
@ -806,9 +809,7 @@ class WalletStateManager:
) )
else: else:
# The rest of the blocks of epoch (using new difficulty and min iters) # The rest of the blocks of epoch (using new difficulty and min iters)
height2 = ( height2 = curr.height - (curr.height % self.constants.DIFFICULTY_EPOCH) - 1
curr.height - (curr.height % self.constants.DIFFICULTY_EPOCH) - 1
)
height1 = height2 - self.constants.DIFFICULTY_EPOCH height1 = height2 - self.constants.DIFFICULTY_EPOCH
assert height2 > 0 assert height2 > 0
@ -824,10 +825,7 @@ class WalletStateManager:
assert iters2 is not None assert iters2 is not None
min_iters_precise = uint64( min_iters_precise = uint64(
(iters2 - iters1) (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. # Truncates to only 12 bits plus 0s. This prevents grinding attacks.
return uint64( return uint64(
@ -996,7 +994,7 @@ class WalletStateManager:
def validate_select_proofs( def validate_select_proofs(
self, self,
all_proof_hashes: List[Tuple[bytes32, Optional[Tuple[uint64, uint64]]]], all_proof_hashes: List[Tuple[bytes32, Optional[uint64], Optional[uint64]]],
heights: List[uint32], heights: List[uint32],
cached_blocks: Dict[bytes32, Tuple[BlockRecord, HeaderBlock, Optional[bytes]]], cached_blocks: Dict[bytes32, Tuple[BlockRecord, HeaderBlock, Optional[bytes]]],
potential_header_hashes: Dict[uint32, bytes32], potential_header_hashes: Dict[uint32, bytes32],
@ -1105,8 +1103,7 @@ class WalletStateManager:
if ( if (
height height
< self.constants.DIFFICULTY_EPOCH < self.constants.DIFFICULTY_EPOCH + self.constants.DIFFICULTY_DELAY
+ self.constants.DIFFICULTY_DELAY
): ):
min_iters = self.constants.MIN_ITERS_STARTING min_iters = self.constants.MIN_ITERS_STARTING
else: else:
@ -1125,11 +1122,11 @@ class WalletStateManager:
height1 = height2 - self.constants.DIFFICULTY_EPOCH height1 = height2 - self.constants.DIFFICULTY_EPOCH
if height1 == -1: if height1 == -1:
iters1 = uint64(0) iters1: Optional[uint64] = uint64(0)
else: else:
iters1 = all_proof_hashes[height1][2] iters1 = all_proof_hashes[height1][2]
assert iters1 is not None
iters2 = all_proof_hashes[height2][2] iters2 = all_proof_hashes[height2][2]
assert iters1 is not None
assert iters2 is not None assert iters2 is not None
min_iters = uint64( min_iters = uint64(

View File

@ -221,7 +221,7 @@ class WalletStore:
regular_rows = await cursor_regular_coins.fetchall() regular_rows = await cursor_regular_coins.fetchall()
await cursor_regular_coins.close() await cursor_regular_coins.close()
for row in coinbase_rows + regular_rows: for row in list(coinbase_rows) + list(regular_rows):
coin = Coin( coin = Coin(
bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7] bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), row[7]
) )
@ -334,7 +334,7 @@ class WalletStore:
if br.height > max_height: if br.height > max_height:
max_height = br.height max_height = br.height
# Makes sure there's exactly one block per 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 return hash_to_br
async def add_block_record(self, block_record: BlockRecord, in_lca_path: bool): async def add_block_record(self, block_record: BlockRecord, in_lca_path: bool):

View File

@ -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_confirmed_balance, 30)
await time_out_assert(15, cc_wallet_2.get_unconfirmed_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 assert TradeStatus(trade.status) is TradeStatus.CONFIRMED
@pytest.mark.asyncio @pytest.mark.asyncio
@ -271,13 +271,15 @@ class TestCCTrades:
wallet_node_b.wallet_state_manager, wallet_b, colour 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 = "test_offer_file.offer"
file_path = Path(file) file_path = Path(file)
if file_path.exists(): if file_path.exists():
file_path.unlink() 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) 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) await time_out_assert(15, cc_b_4.get_confirmed_balance, 60)
async def assert_func(): async def assert_func():
assert trade_a is not None
trade = await trade_manager_a.get_trade_by_id(trade_a.trade_id) trade = await trade_manager_a.get_trade_by_id(trade_a.trade_id)
assert trade is not None
return trade.status return trade.status
async def assert_func_b(): async def assert_func_b():
assert offer is not None
trade = await trade_manager_b.get_trade_by_id(offer.trade_id) trade = await trade_manager_b.get_trade_by_id(offer.trade_id)
assert trade is not None
return trade.status return trade.status
await time_out_assert(15, assert_func, TradeStatus.CONFIRMED.value) await time_out_assert(15, assert_func, TradeStatus.CONFIRMED.value)
@ -342,6 +348,7 @@ class TestCCTrades:
assert spendable_chia == spendable_after_cancel_1 assert spendable_chia == spendable_after_cancel_1
trade_a = await trade_manager_a.get_trade_by_id(trade_offer.trade_id) 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 assert trade_a.status == TradeStatus.CANCELED.value
@pytest.mark.asyncio @pytest.mark.asyncio
@ -392,7 +399,9 @@ class TestCCTrades:
# Spendable should be the same as it was before making offer 1 # Spendable should be the same as it was before making offer 1
async def get_status(): async def get_status():
assert trade_offer is not None
trade_a = await trade_manager_a.get_trade_by_id(trade_offer.trade_id) trade_a = await trade_manager_a.get_trade_by_id(trade_offer.trade_id)
assert trade_a is not None
return trade_a.status return trade_a.status
await time_out_assert(15, get_status, TradeStatus.CANCELED.value) await time_out_assert(15, get_status, TradeStatus.CANCELED.value)

View File

@ -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 "Starting local blockchain simulation. Runs a local introducer and chia system."
echo "Note that this simulation will not work if connected to external nodes." echo "Note that this simulation will not work if connected to external nodes."

View File

@ -111,9 +111,7 @@ class TestCoinStore:
async def test_basic_reorg(self): async def test_basic_reorg(self):
initial_block_count = 20 initial_block_count = 20
reorg_length = 15 reorg_length = 15
blocks = bt.get_consecutive_blocks( blocks = bt.get_consecutive_blocks(test_constants, initial_block_count, [], 9)
test_constants, initial_block_count, [], 9
)
db_path = Path("blockchain_test.db") db_path = Path("blockchain_test.db")
if db_path.exists(): if db_path.exists():
db_path.unlink() db_path.unlink()

View File

@ -865,10 +865,7 @@ class TestWalletProtocol:
hashes = msgs[0].message.data.hashes hashes = msgs[0].message.data.hashes
assert len(hashes) >= len(blocks_list) - 2 assert len(hashes) >= len(blocks_list) - 2
for i in range(len(hashes)): for i in range(len(hashes)):
if ( if i % test_constants.DIFFICULTY_EPOCH == test_constants.DIFFICULTY_DELAY:
i % test_constants.DIFFICULTY_EPOCH
== test_constants.DIFFICULTY_DELAY
):
assert hashes[i][1] is not None assert hashes[i][1] is not None
elif i > 0: elif i > 0:
assert hashes[i][1] is None assert hashes[i][1] is None

View File

@ -111,8 +111,13 @@ class TestRpc:
res_2 = await client_2.get_plots() res_2 = await client_2.get_plots()
assert len(res_2["plots"]) == num_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)) 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() res_2 = await client_2.get_plots()
assert len(res_2["plots"]) == num_plots + 1 assert len(res_2["plots"]) == num_plots + 1
@ -120,6 +125,10 @@ class TestRpc:
res_3 = await client_2.get_plots() res_3 = await client_2.get_plots()
assert len(res_3["plots"]) == num_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: except AssertionError:
# Checks that the RPC manages to stop the node # Checks that the RPC manages to stop the node
client.close() client.close()

View File

@ -260,7 +260,11 @@ async def setup_harvester(port, farmer_port, consensus_constants: ConsensusConst
await run_task 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 = load_config(bt.root_path, "config.yaml", "farmer")
config_pool = load_config(bt.root_path, "config.yaml", "pool") config_pool = load_config(bt.root_path, "config.yaml", "pool")
@ -357,7 +361,9 @@ async def setup_vdf_clients(port):
await kill_processes() 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 = load_config(bt.root_path, "config.yaml", "timelord")
config["sanitizer_mode"] = sanitizer config["sanitizer_mode"] = sanitizer
if sanitizer: if sanitizer:
@ -424,12 +430,16 @@ async def setup_two_nodes(consensus_constants: ConsensusConstants):
await _teardown_nodes(node_iters) 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 = [ node_iters = [
setup_full_node( setup_full_node(
consensus_constants, "blockchain_test.db", 21234, simulator=False 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__() full_node, s1 = await node_iters[0].__anext__()
@ -459,7 +469,11 @@ async def setup_simulators_and_wallets(
seed = bytes(uint32(index)) seed = bytes(uint32(index))
port = 55000 + index port = 55000 + index
wlt = setup_wallet_node( 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__()) wallets.append(await wlt.__anext__())
node_iters.append(wlt) node_iters.append(wlt)