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:
name: MacOS-latest on Pyton 3.7
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 4

View File

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

View File

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

View File

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

View File

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

View File

@ -9,18 +9,35 @@ for setuptools_scm/PEP 440 reasons.
## [Unreleased]
### Added
- See wallet balances in command line: chia show -w
- Retry opening invalid plots every 20 minutes (so you can cp a large plot)
- "chia keys sign" and "chia keys verify"
- Windows BLS Signature library now uses libsodium for additional security.
- Wheels for ARM64/aarch64 also build for python 3.7.
- See and remove plot directories from UI and command line
- Specify memory buffer in UI
### Changed
- chia start wallet-server changed to chia start wallet, for consistency.
- All data size units are displayed in GiB instead of GB (powers of 1024 instead of 1000)
- Better error messages for restoring wallet from mnemonic
### Fixed
- Fixed open_connection not being cancelled when node exits
- Increase the robustness of node and wallet shutdown
- Handle disconnection and reconnection of hard drives properly
- Addressed pre-Haswell Windows signatures failing.
- MacOS, Linux x64, and Linux aarch64 were not correctly compiling libsodium in
the blspy/bls-signatures library.
- Removed outdated "200 plots" language from Plot tab.
- Fixed spelling error for "folder" on Plot tab.
- Various node dependency security vulnerabilities have been fixed.
- Request peers was not returning currently connected peers older than 1 day
### Deprecated
- Removed legacy scripts such as chia-stop-server, chia-restart-harvester, etc.
## [1.0beta8] aka Beta 1.8 - 2020-07-16

View File

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

View File

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

View File

@ -145,7 +145,7 @@ const closeDaemon = callback => {
if (!called_cb) {
callback();
}
}, 15000);
}, 20000);
};
const exitPyProc = e => {};
@ -214,29 +214,17 @@ const createWindow = () => {
// }
mainWindow.on("close", e => {
if (decidedToClose) {
if (pyProc != null) {
if (process.platform === "win32") {
process.stdout.write("Killing daemon on windows");
var cp = require("child_process");
cp.execSync("taskkill /PID " + pyProc.pid + " /T /F");
} else {
process.stdout.write("Killing daemon on other platforms");
pyProc.kill();
pyProc = null;
pyPort = null;
}
}
return;
}
e.preventDefault();
var choice = require("electron").dialog.showMessageBoxSync({
type: "question",
buttons: ["Yes", "No"],
buttons: ["No", "Yes"],
title: "Confirm",
message:
"Are you sure you want to quit? GUI Plotting and farming will stop."
});
if (choice == 1) {
if (choice == 0) {
return;
}
mainWindow.loadURL(closingUrl);

View File

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

View File

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

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

View File

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

View File

@ -9,7 +9,8 @@ const initial_state = {
harvester: {
plots: [],
not_found_filenames: [],
failed_to_open_filenames: []
failed_to_open_filenames: [],
plot_directories: []
}
};
@ -69,6 +70,14 @@ export const farmingReducer = (state = { ...initial_state }, action) => {
return state;
}
if (command === "get_plot_directories") {
if (data.success !== true) {
return state;
}
state.harvester.plot_directories = data.directories;
return state;
}
return state;
default:
return state;

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -256,7 +256,7 @@ const getStatusItems = (state, connected) => {
status_items.push(min_item);
const space =
(BigInt(state.space) / BigInt(Math.pow(1024, 4))).toString() + "TB";
(BigInt(state.space) / BigInt(Math.pow(1024, 4))).toString() + "TiB";
const space_item = {
label: "Estimated network space",
value: space,

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 CssBaseline from "@material-ui/core/CssBaseline";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { withTheme } from "@material-ui/styles";
import Container from "@material-ui/core/Container";
import ArrowBackIosIcon from "@material-ui/icons/ArrowBackIos";
@ -10,9 +9,9 @@ import { connect, useSelector, useDispatch } from "react-redux";
import { genereate_mnemonics, add_new_key_action } from "../modules/message";
import { withRouter } from "react-router-dom";
import CssTextField from "../components/cssTextField";
import myStyle from "./style";
import { changeEntranceMenu, presentSelectKeys } from "../modules/entranceMenu";
import { openDialog } from "../modules/dialogReducer";
import logo from "../assets/img/chia_logo.svg";
import myStyle from "./style";
const MnemonicField = props => {
return (
@ -48,17 +47,6 @@ const UIPart = props => {
words = [];
}
useEffect(() => {
dispatch(
openDialog(
"Welcome!",
`The following words are used for your wallet backup.
Without them, you will lose access to your wallet, keep them safe!!!
Write down each word along with the order number next to them. (Order is important) `
)
);
}, [dispatch]);
function goBack() {
dispatch(changeEntranceMenu(presentSelectKeys));
}
@ -73,10 +61,15 @@ const UIPart = props => {
{" "}
</ArrowBackIosIcon>
<div className={classes.grid_wrap}>
<img className={classes.logo} src={logo} alt="Logo" />
<Container className={classes.grid} maxWidth="lg">
<Typography className={classes.title} component="h4" variant="h4">
New Wallet
</Typography>
<h1 className={classes.titleSmallMargin}>New Wallet</h1>
<p className={classes.whiteP}>
Welcome! The following words are used for your wallet backup.
Without them, you will lose access to your wallet, keep them safe!
Write down each word along with the order number next to them.
(Order is important)
</p>
<Grid container spacing={2}>
<Iterator mnemonic={words}></Iterator>
</Grid>

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

View File

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

View File

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

View File

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

View File

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

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_farmer = "chia_farmer";
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 = [
"aiter==0.13.20191203", # Used for async generator tools
"blspy==0.2.0", # Signature library
"chiavdf==0.12.22", # timelord and vdf verification
"blspy==0.2.1", # Signature library
"chiavdf==0.12.23", # timelord and vdf verification
"chiabip158==0.16", # bip158-style wallet filters
"chiapos==0.12.23", # proof of space
"chiapos==0.12.24", # proof of space
"clvm==0.4", # contract language
"clvm-tools==0.1.1", # clvm compiler tools
"aiohttp==3.6.2", # HTTP server for full node rpc
@ -69,18 +69,10 @@ kwargs = dict(
"src.wallet.trading",
"src.ssl",
],
scripts=[
"scripts/_chia-common",
"scripts/_chia-stop-wallet",
"scripts/chia-drop-db",
"scripts/chia-restart-harvester",
"scripts/chia-start-sim",
"scripts/chia-stop-all",
],
entry_points={
"console_scripts": [
"chia = src.cmds.chia:main",
"chia-wallet = src.server.start_wallet:main",
"chia_wallet = src.server.start_wallet:main",
"chia_full_node = src.server.start_full_node:main",
"chia_harvester = src.server.start_harvester:main",
"chia_farmer = src.server.start_farmer:main",

View File

@ -1,4 +1,8 @@
from pathlib import Path
from typing import List
from blspy import AugSchemeMPL, G1Element, G2Element
from src.cmds.init import check_keys
from src.util.keychain import (
generate_mnemonic,
@ -20,6 +24,8 @@ command_list = [
"add",
"delete",
"delete_all",
"sign",
"verify",
]
@ -35,6 +41,12 @@ def help_message():
"chia keys delete -f [fingerprint] (delete a key by it's pk fingerprint in hex form)"
)
print("chia keys delete_all (delete all private keys in keychain)")
print(
"chia keys sign -f [fingerprint] -t [hd_path] -d [message] (sign a message with a private key)"
)
print(
"chia keys verify -p [public_key] -d [message] -s [signature] (verify a signature with a pk)"
)
def make_parser(parser):
@ -54,7 +66,31 @@ def make_parser(parser):
"--fingerprint",
type=int,
default=None,
help="Enter the fingerprint of the key you want to delete",
help="Enter the fingerprint of the key you want to use",
)
parser.add_argument(
"-t",
"--hd_path",
type=str,
default=None,
help="Enter the HD path in the form 'm/12381/8444/n/n'",
)
parser.add_argument(
"-d",
"--message",
type=str,
default=None,
help="Enter the message to sign in UTF-8",
)
parser.add_argument(
"-p", "--public_key", type=str, default=None, help="Enter the pk in hex",
)
parser.add_argument(
"-s", "--signature", type=str, default=None, help="Enter the signature in hex",
)
parser.add_argument(
@ -76,9 +112,8 @@ def generate_and_print():
"""
mnemonic = generate_mnemonic()
mnemonics_string = mnemonic_to_string(mnemonic)
print("Generating private key. Mnemonic:")
print(mnemonics_string)
print("Generating private key. Mnemonic (24 secret words):")
print(mnemonic)
print(
"Note that this key has not been added to the keychain. Run chia keys add_seed -m [MNEMONICS] to add"
)
@ -107,30 +142,13 @@ def add_private_key_seed(mnemonic):
print(
f"Added private key with public key fingerprint {fingerprint} and mnemonic"
)
print(f"{mnemonic_to_string(mnemonic)}")
print(mnemonic)
except ValueError as e:
print(e)
return
def mnemonic_to_string(mnemonic_str):
"""
Converts a menmonic to a user readable string in the terminal.
"""
mnemonic = mnemonic_str.split()
mnemonics_string = ""
for i in range(0, len(mnemonic)):
mnemonics_string += f"{i + 1}) {mnemonic[i]}"
if i != len(mnemonic) - 1:
mnemonics_string += ", "
if (i + 1) % 6 == 0:
mnemonics_string += "\n"
return mnemonics_string
def show_all_keys():
"""
Prints all keys and mnemonics (if available).
@ -163,9 +181,8 @@ def show_all_keys():
)
assert seed is not None
mnemonic = bytes_to_mnemonic(seed)
mnemonic_string = mnemonic_to_string(mnemonic)
print(" Mnemonic seed:")
print(mnemonic_string)
print(" Mnemonic seed (24 secret words):")
print(mnemonic)
def delete(args):
@ -182,6 +199,55 @@ def delete(args):
keychain.delete_key_by_fingerprint(fingerprint)
def sign(args):
if args.message is None:
print("Please specify the message argument -d")
quit()
if args.fingerprint is None or args.hd_path is None:
print("Please specify the fingerprint argument -f and hd_path argument -t")
quit()
message = args.message
assert message is not None
k = Keychain()
private_keys = k.get_all_private_keys()
fingerprint = args.fingerprint
assert fingerprint is not None
hd_path = args.hd_path
assert hd_path is not None
path: List[uint32] = [uint32(int(i)) for i in hd_path.split("/") if i != "m"]
for sk, _ in private_keys:
if sk.get_g1().get_fingerprint() == fingerprint:
for c in path:
sk = sk.derive_child(c)
print("Public key:", sk.get_g1())
print("Signature:", AugSchemeMPL.sign(sk, bytes(message, "utf-8")))
return
print(f"Fingerprint {fingerprint} not found in keychain")
def verify(args):
if args.message is None:
print("Please specify the message argument -d")
quit()
if args.public_key is None:
print("Please specify the public_key argument -p")
quit()
if args.signature is None:
print("Please specify the signature argument -s")
quit()
assert args.message is not None
assert args.public_key is not None
assert args.signature is not None
message = bytes(args.message, "utf-8")
public_key = G1Element.from_bytes(bytes.fromhex(args.public_key))
signature = G2Element.from_bytes(bytes.fromhex(args.signature))
print(AugSchemeMPL.verify(public_key, message, signature))
def handler(args, parser):
if args.command is None or len(args.command) < 1:
help_message()
@ -213,3 +279,7 @@ def handler(args, parser):
keychain.delete_all_keys()
if command == "generate_and_print":
generate_and_print()
if command == "sign":
sign(args)
if command == "verify":
verify(args)

View File

@ -101,7 +101,7 @@ async def netstorge_async(args, parser):
network_space_terrabytes_estimate = network_space_bytes_estimate / 1024 ** 4
print(
f"The elapsed time between blocks is reported as {time_delta}.\n"
f"The network has an estimated {network_space_terrabytes_estimate:.2f}TB"
f"The network has an estimated {network_space_terrabytes_estimate:.2f}TiB"
)
except Exception as e:

View File

@ -1,6 +1,10 @@
from pathlib import Path
import logging
from src.plotting.plot_tools import add_plot_directory
from src.plotting.plot_tools import (
add_plot_directory,
remove_plot_directory,
get_plot_directories,
)
from src.plotting.create_plots import create_plots
from src.plotting.check_plots import check_plots
from src.util.logging import initialize_logging
@ -9,11 +13,7 @@ from src.util.logging import initialize_logging
log = logging.getLogger(__name__)
command_list = [
"create",
"check",
"add",
]
command_list = ["create", "check", "add", "remove", "show"]
def help_message():
@ -21,12 +21,14 @@ def help_message():
print(f"command can be any of {command_list}")
print("")
print(
"chia plots create -k [size] -n [number of plots] -b [memory buffer size MB]"
"chia plots create -k [size] -n [number of plots] -b [memory buffer size MiB]"
+ " -f [farmer pk] -p [pool pk] -t [tmp dir] -2 [tmp dir 2] -d [final dir] (creates plots)"
)
print("-s [sk_seed] -i [index] are available for debugging")
print("chia plots check -n [num checks] (checks plots)")
print("chia plots add -d [directory] (adds a directory of plots)")
print("chia plots remove -d [directory] (removes a directory of plots from config)")
print("chia plots show (shows the directory of current plots)")
def make_parser(parser):
@ -35,10 +37,7 @@ def make_parser(parser):
"-n", "--num", help="Number of plots or challenges", type=int, default=None
)
parser.add_argument(
"-i", "--index", help="First plot index", type=int, default=None
)
parser.add_argument(
"-b", "--buffer", help="Megabytes for sort/plot buffer", type=int, default=2048
"-b", "--buffer", help="Mebibytes for sort/plot buffer", type=int, default=2000
)
parser.add_argument(
"-f",
@ -50,9 +49,6 @@ def make_parser(parser):
parser.add_argument(
"-p", "--pool_public_key", help="Hex public key of pool", type=str, default=None
)
parser.add_argument(
"-s", "--sk_seed", help="Secret key seed in hex", type=str, default=None
)
parser.add_argument(
"-t",
"--tmp_dir",
@ -85,6 +81,19 @@ def make_parser(parser):
parser.print_help = lambda self=parser: help_message()
def show(root_path):
print("Directories where plots are being searched for:")
print("Note that subdirectories must be added manually.")
print(
"Add with 'chia plots add -d [dir]' and remove with"
+ " 'chia plots remove -d [dir]'."
+ " Scan and check plots with 'chia plots check'"
)
print()
for str_path in get_plot_directories(root_path):
print(f"{str_path}")
def handler(args, parser):
if args.command is None or len(args.command) < 1:
help_message()
@ -109,3 +118,8 @@ def handler(args, parser):
elif command == "add":
str_path = args.final_dir
add_plot_directory(str_path, root_path)
elif command == "remove":
str_path = args.final_dir
remove_plot_directory(str_path, root_path)
elif command == "show":
show(root_path)

View File

@ -3,15 +3,17 @@ import asyncio
import time
from time import struct_time, localtime
from typing import List, Optional
from typing import List, Optional, Dict
from src.server.connection import NodeType
from src.types.header_block import HeaderBlock
from src.rpc.full_node_rpc_client import FullNodeRpcClient
from src.rpc.wallet_rpc_client import WalletRpcClient
from src.util.byte_types import hexstr_to_bytes
from src.util.config import str2bool
from src.util.config import load_config
from src.util.default_root import DEFAULT_ROOT_PATH
from src.cmds.units import units
def make_parser(parser):
@ -86,6 +88,26 @@ def make_parser(parser):
type=int,
default=8555,
)
parser.add_argument(
"-wp",
"--wallet-rpc-port",
help="Set the port where the Wallet is hosting the RPC interface."
+ " See the rpc_port under wallet in config.yaml."
+ "Defaults to 9256",
type=int,
default=9256,
)
parser.add_argument(
"-w",
"--wallet-balances",
help="Display wallet balances.",
type=str2bool,
nargs="?",
const=True,
default=False,
)
parser.set_defaults(function=show)
@ -178,7 +200,7 @@ async def show_async(args, parser):
print("Connections")
print(
"Type IP Ports NodeID Last Connect"
+ " MB Up|Dwn"
+ " MiB Up|Dwn"
)
for con in connections:
last_connect_tuple = struct_time(localtime(con["last_message_time"]))
@ -262,7 +284,7 @@ async def show_async(args, parser):
if block.header.data.aggregated_signature is None:
aggregated_signature = block.header.data.aggregated_signature
else:
aggregated_signature = block.header.data.aggregated_signature.sig
aggregated_signature = block.header.data.aggregated_signature
print("Block", block.header.data.height, ":")
print(
f"Header Hash 0x{args.block_by_header_hash}\n"
@ -289,6 +311,55 @@ async def show_async(args, parser):
else:
print("Block with header hash", args.block_by_header_hash, "not found.")
if args.wallet_balances:
if "wallet_rpc_port" not in args or args.wallet_rpc_port is None:
wallet_rpc_port = config["wallet"]["rpc_port"]
else:
wallet_rpc_port = args.wallet_rpc_port
wallet_client = await WalletRpcClient.create(self_hostname, wallet_rpc_port)
summaries: Dict = await wallet_client.get_wallet_summaries()
print("Balances")
for wallet_id, summary in summaries.items():
balances = await wallet_client.get_wallet_balance(wallet_id)
if "name" in summary:
print(
f"Wallet ID {wallet_id} type {summary['type']} {summary['name']}"
)
print(
f" -Confirmed: {balances['confirmed_wallet_balance']/units['colouredcoin']}"
)
print(
f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}"
)
print(
f" -Spendable: {balances['spendable_balance']/units['colouredcoin']}"
)
print(
f" -Frozen: {balances['frozen_balance']/units['colouredcoin']}"
)
print(
f" -Pending change: {balances['pending_change']/units['colouredcoin']}"
)
else:
print(f"Wallet ID {wallet_id} type {summary['type']}")
print(
f" -Confirmed: {balances['confirmed_wallet_balance']/units['chia']} TXCH"
)
print(
f" -Unconfirmed: {balances['unconfirmed_wallet_balance']/units['chia']} TXCH"
)
print(
f" -Spendable: {balances['spendable_balance']/units['chia']} TXCH"
)
print(
f" -Frozen: {balances['frozen_balance']/units['chia']} TXCH"
)
print(
f" -Pending change: {balances['pending_change']/units['chia']} TXCH"
)
wallet_client.close()
await wallet_client.await_closed()
except Exception as e:
if isinstance(e, aiohttp.client_exceptions.ClientConnectorError):
print(f"Connection error. Check if full node is running at {args.rpc_port}")

9
src/cmds/units.py Normal file
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_FREEZE_PERIOD": 200,
# Max coin amount uint(1 << 64)
"MAX_COIN_AMOUNT": 0xffffffffffffffff,
"MAX_COIN_AMOUNT": 0xFFFFFFFFFFFFFFFF,
# Raw size per block target = 1,000,000 bytes
# Rax TX (single in, single out) = 219 bytes (not compressed)
# TX = 457 vBytes

View File

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

View File

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

View File

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

View File

@ -1722,7 +1722,11 @@ class FullNode:
) -> OutboundMessageGenerator:
if self.global_connections is None:
return
peers = self.global_connections.peers.get_peers(recent_threshold=24 * 60 * 60)
connected_peers = self.global_connections.get_full_node_peerinfos()
unconnected_peers = self.global_connections.peers.get_peers(
recent_threshold=24 * 60 * 60
)
peers = list(set(connected_peers + unconnected_peers))
yield OutboundMessage(
NodeType.FULL_NODE,

View File

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

View File

@ -28,7 +28,7 @@ def check_plots(args, root_path):
for pk in config["farmer"]["pool_public_keys"]
]
_, provers, failed_to_open_filenames, no_key_filenames = load_plots(
{}, set(), pks, pool_public_keys, root_path, open_no_key_filenames=True,
{}, {}, pks, pool_public_keys, root_path, open_no_key_filenames=True,
)
if len(provers) > 0:
log.info("")
@ -78,7 +78,7 @@ def check_plots(args, root_path):
log.info("Summary")
total_plots: int = sum(list(total_good_plots.values()))
log.info(
f"Found {total_plots} valid plots, total size {total_size / (1024 * 1024 * 1024 * 1024):.5f} TB"
f"Found {total_plots} valid plots, total size {total_size / (1024 * 1024 * 1024 * 1024):.5f} TiB"
)
for (k, count) in sorted(dict(total_good_plots).items()):
log.info(f"{count} plots of size {k}")

View File

@ -3,6 +3,7 @@ from pathlib import Path
from blspy import PrivateKey, G1Element
from chiapos import DiskProver
from dataclasses import dataclass
import time
import logging
import traceback
from src.types.proof_of_space import ProofOfSpace
@ -70,7 +71,7 @@ def stream_plot_info(
return data
def add_plot_directory(str_path, root_path):
def add_plot_directory(str_path: str, root_path: Path) -> Dict:
config = load_config(root_path, "config.yaml")
if str(Path(str_path).resolve()) not in config["harvester"]["plot_directories"]:
config["harvester"]["plot_directories"].append(str(Path(str_path).resolve()))
@ -78,14 +79,38 @@ def add_plot_directory(str_path, root_path):
return config
def get_plot_directories(root_path: Path) -> List[str]:
config = load_config(root_path, "config.yaml")
return [
str(Path(str_path).resolve())
for str_path in config["harvester"]["plot_directories"]
]
def remove_plot_directory(str_path: str, root_path: Path) -> None:
config = load_config(root_path, "config.yaml")
str_paths: List[str] = config["harvester"]["plot_directories"]
# If path str matches exactly, remove
if str_path in str_paths:
str_paths.remove(str_path)
# If path matcehs full path, remove
new_paths = [Path(sp).resolve() for sp in str_paths]
if Path(str_path).resolve() in new_paths:
new_paths.remove(Path(str_path).resolve())
config["harvester"]["plot_directories"] = [str(np) for np in new_paths]
save_config(root_path, "config.yaml", config)
def load_plots(
provers: Dict[Path, PlotInfo],
failed_to_open_filenames: Set[Path],
failed_to_open_filenames: Dict[Path, int],
farmer_public_keys: Optional[List[G1Element]],
pool_public_keys: Optional[List[G1Element]],
root_path: Path,
open_no_key_filenames=False,
) -> Tuple[bool, Dict[Path, PlotInfo], Set[Path], Set[Path]]:
) -> Tuple[bool, Dict[Path, PlotInfo], Dict[Path, int], Set[Path]]:
config_file = load_config(root_path, "config.yaml", "harvester")
changed = False
no_key_filenames: Set[Path] = set()
@ -96,16 +121,22 @@ def load_plots(
for paths in plot_filenames.values():
all_filenames += paths
total_size = 0
new_provers: Dict[Path, PlotInfo] = {}
for filename in all_filenames:
if filename.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
if filename in failed_to_open_filenames:
continue
if filename.exists():
try:
prover = DiskProver(str(filename))
(
@ -141,7 +172,7 @@ def load_plots(
plot_public_key: G1Element = ProofOfSpace.generate_plot_public_key(
local_sk.get_g1(), farmer_public_key
)
provers[filename] = PlotInfo(
new_provers[filename] = PlotInfo(
prover,
pool_public_key,
farmer_public_key,
@ -155,13 +186,13 @@ def load_plots(
except Exception as e:
tb = traceback.format_exc()
log.error(f"Failed to open file {filename}. {e} {tb}")
failed_to_open_filenames.add(filename)
failed_to_open_filenames[filename] = int(time.time())
continue
log.info(
f"Found plot {filename} of size {provers[filename].prover.get_size()}"
f"Found plot {filename} of size {new_provers[filename].prover.get_size()}"
)
log.info(
f"Loaded a total of {len(provers)} plots of size {total_size / (1024 ** 4)} TB"
f"Loaded a total of {len(new_provers)} plots of size {total_size / (1024 ** 4)} TiB"
)
return (changed, provers, failed_to_open_filenames, no_key_filenames)
return (changed, new_provers, failed_to_open_filenames, no_key_filenames)

View File

@ -15,6 +15,8 @@ class HarvesterRpcApi:
"/refresh_plots": self.refresh_plots,
"/delete_plot": self.delete_plot,
"/add_plot_directory": self.add_plot_directory,
"/get_plot_directories": self.get_plot_directories,
"/remove_plot_directory": self.remove_plot_directory,
}
async def _state_changed(self, change: str) -> List[str]:
@ -46,3 +48,12 @@ class HarvesterRpcApi:
dirname = request["dirname"]
success = await self.service._add_plot_directory(dirname)
return {"success": success}
async def get_plot_directories(self, request: Dict) -> Dict:
plot_dirs = await self.service._get_plot_directories()
return {"success": True, "directories": plot_dirs}
async def remove_plot_directory(self, request: Dict) -> Dict:
dirname = request["dirname"]
success = await self.service._remove_plot_directory(dirname)
return {"success": success}

View File

@ -21,4 +21,12 @@ class HarvesterRpcClient(RpcClient):
return await self.fetch("delete_plot", {"filename": filename})
async def add_plot_directory(self, dirname: str) -> bool:
return await self.fetch("add_plot_directory", {"dirname": dirname})
return (await self.fetch("add_plot_directory", {"dirname": dirname}))["success"]
async def get_plot_directories(self) -> List[str]:
return (await self.fetch("get_plot_directories", {}))["directories"]
async def remove_plot_directory(self, dirname: str) -> bool:
return (await self.fetch("remove_plot_directory", {"dirname": dirname}))[
"success"
]

View File

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

View File

@ -35,7 +35,7 @@ log = logging.getLogger(__name__)
class WalletRpcApi:
def __init__(self, wallet_node: WalletNode):
self.service = wallet_node
self.service_name = "chia-wallet"
self.service_name = "chia_wallet"
def get_routes(self) -> Dict[str, Callable]:
return {
@ -137,7 +137,7 @@ class WalletRpcApi:
}
if wallet_id is not None:
data["wallet_id"] = wallet_id
return [create_payload("state_changed", data, "chia-wallet", "wallet_ui")]
return [create_payload("state_changed", data, "chia_wallet", "wallet_ui")]
async def get_next_puzzle_hash(self, request: Dict) -> Dict:
"""
@ -574,7 +574,22 @@ class WalletRpcApi:
# Adding a key from 24 word mnemonic
mnemonic = request["mnemonic"]
passphrase = ""
sk = self.service.keychain.add_private_key(" ".join(mnemonic), passphrase)
try:
sk = self.service.keychain.add_private_key(
" ".join(mnemonic), passphrase
)
except KeyError as e:
return {
"success": False,
"error": f"The word '{e.args[0]}' is incorrect.'",
"word": e.args[0],
}
except ValueError as e:
return {
"success": False,
"error": e.args[0],
}
else:
return {"success": False}
@ -609,7 +624,8 @@ class WalletRpcApi:
fingerprint = request["fingerprint"]
self.service.keychain.delete_key_by_fingerprint(fingerprint)
path = path_from_root(
self.service.root_path, f"{self.service.config['database_path']}-{fingerprint}"
self.service.root_path,
f"{self.service.config['database_path']}-{fingerprint}",
)
if path.exists():
path.unlink()

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
log = logging.getLogger(__name__)
async def initialize_pipeline(
srwt_aiter,
api: Any,
@ -161,8 +164,11 @@ async def connection_to_outbound(
"""
connection, global_connections = pair
if connection.on_connect:
try:
async for outbound_message in connection.on_connect():
yield connection, outbound_message, global_connections
except Exception as e:
connection.log.warning(f"Exception in on_connect: {e}")
async def perform_handshake(
@ -228,7 +234,7 @@ async def perform_handshake(
)
# Only yield a connection if the handshake is succesful and the connection is not a duplicate.
yield connection, global_connections
except (ProtocolError, asyncio.IncompleteReadError, OSError, Exception,) as e:
except Exception as e:
connection.log.warning(f"{e}, handshake not completed. Connection not created.")
# Make sure to close the connection even if it's not in global connections
connection.close()

View File

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

View File

@ -90,6 +90,7 @@ class Service:
assert network_id is not None
self._node_type = node_type
self._service_name = service_name
proctitle_name = f"chia_{service_name}"
setproctitle(proctitle_name)
@ -186,41 +187,51 @@ class Service:
except NotImplementedError:
self._log.info("signal handlers unsupported")
for _ in self._server_sockets:
await _.wait_closed()
await self._server.await_closed()
if self._stop_callback:
self._stop_callback()
if self._await_closed_callback:
await self._await_closed_callback()
self._task = asyncio.create_task(_run())
async def run(self):
self.start()
await self.wait_closed()
self._log.info("Closed all node servers.")
return 0
def stop(self):
if not self._is_stopping:
self._is_stopping = True
self._log.info("Calling service stop callback")
if self._stop_callback:
self._stop_callback()
self._log.info("Closing server sockets")
for _ in self._server_sockets:
_.close()
self._log.info("Cancelling reconnect task")
for _ in self._reconnect_tasks:
_.cancel()
self._log.info("Closing connections")
self._server.close_all()
self._api._shut_down = True
self._log.info("Stopping introducer task")
if self._introducer_poll_task:
self._introducer_poll_task.cancel()
async def wait_closed(self):
await self._task
self._log.info("Waiting for socket to be closed (if opened)")
for _ in self._server_sockets:
await _.wait_closed()
self._log.info("Waiting for ChiaServer to be closed")
await self._server.await_closed()
if self._rpc_task:
self._log.info("Waiting for RPC server")
await (await self._rpc_task)()
self._log.info("Closed RPC server.")
self._log.info(f"Service at port {self._advertised_port} fully closed")
if self._await_closed_callback:
self._log.info("Waiting for service _await_closed callback")
await self._await_closed_callback()
self._log.info(
f"Service {self._service_name} at port {self._advertised_port} fully closed"
)
async def async_run_service(*args, **kwargs):

View File

@ -15,7 +15,7 @@ test_constants = make_test_constants_without_genesis(
"NUMBER_ZERO_BITS_CHALLENGE_SIG": 1,
"CLVM_COST_RATIO_CONSTANT": 108,
"COINBASE_FREEZE_PERIOD": 0,
"GENESIS_BLOCK": b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x883|\xde\xeb0\x9e[1:\x97\xe1Jp\x1e\x85|r9 Q\x81*\x058\xe9\xef\xeb\x89\x0c\x8b\xaft\xe6T\xfd\xf2W[\x01\xf0\x16\xc4\n`\xeb\xbd%\x84m\x82\xb5\xc6\xaa\xe8p\x8e\x8f\xa1\x814Rw\xa2\x9b\xce\xd2\x8a\x94\xfc\xb8\x90o\\\xad:;\x12\x9f\xac\xe2f\xe0\x13\x00\xe9]\x83\xa3\xb6\x8f\x15\x8ds\x91\xdd\x12\x00\x00\x00\x90\x9b2K\xbd\x14\xbc\x85/\x06\xad\xc4\xa0%H\xd5\x81\x0b\xe8\x0e\x0c+\xa3W^(\x84e\xb5\xa9I*\xc7L\xb2\x83L\xc4\xca\xe2\xa9\x9a\xc6\xba\xc7\x9b"s\xd0O\xf9\x06&,4\x82tX-I[\xaf\t6Na\x01\x19!;\xbb\x13\x0b\x9bT\x19\x8c\xf6Q2q\x19\taYhL\xc84(\xaa6\xfb\x9aA\xfc\x06\xc7#\xab\xe0\x1c\x843\xf3\xf7k\xd0\xa9\x087N\x1d\x92\x97<o3+\x10J\xf5\xad\xdc\xfb\xa5\x8b[t\xea\xcd\x00uB\xcf\x1e\xf8\xb7\x15\xd6=\x8b;\xc0\xf0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x05\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\r\xc0\x88F\r6\x84\x9d\x85\xc1\xa2\x9aq\xf7\x1cFXm\xf5L\x9f \xbb\x1d\x10\x97\x84\xb5$\x01\xc9^\x7f2\xfb\xee\xeb\x85\xa6\x02\xe3l\xba\xee+\xa25\x00\xd4\xe1\x90/\x08\x97\xef\x18#\xb7\xe5+\xb2\xb9\xc8\xb9\xce\xe1\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06\xf8\xa8}\xd4_\x98QR\x14"\x06<\xa4;\x9f2\xd3\xed\xd0mK\xf7\xfa~<\x88B\x8b\xd4F\xdcHp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\xc1\xef\x962\x87V\xc4\xd5\xd5AL\x12\x1c\xe4|{\xc0\x7f{.\xe98\x93%\x08:\xa66\xe7\x80Q\x00\x00\x01\xd1\xa9J \x00+[\xbbW\xc5\xdaVh\x93\x16Q\xaa\r\x12\t"\xe3?:fk\xfd\x1f\x82q\xd0\r\x02U\xcc\xf9\xdb\x00\x00\x00\x00\xa3\xaa\xa0\xe0\xediiw+\xae\xecY\r{4\x9f\xc5\x82\'"\x9d\x8dI\x03Y{\x82\xd7pY\xb5\xd7\xc8\x0c-9\xff\xb141\xa5\xee\x17\x01\x1b\x0b&t\xa9\xf6\xe9\xeb\x8a\xc4\x03l\x87\xd0G\x11\x05\xb4\xfap\xe0\x11\xd3W\xcf.\xdb\xc1ye(\xd5A\xc9\x8ds\x83\xaf\xab$\xfd\xf5\x93\xc4,\x7fX\xcf\'\xd7\xa5\xb1\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xb2{\x8c\xa7`a42\xf5\xaa\x87\xa0\rQQB\x1ah*\x07y\xd83k\xf3\xf3\xb6\x8b\x01\xde\xe7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xafw\x92n\x02V\xb3:\x8d\xd4P\x19~\x0b\xa2\x8e\xf5\xaf\'\x1f\xc3\xd8K\xfd\xfd\xc9\x837\x84\x00\xe7K\x06#E\xda\x86B\xf7Q\xb3\xb2\x97\xea\xbd\x96\xdb\xcc\xa1\x91\x87\x04\x1fHqJ\xa2\xbc2\xcc\x04\xacn\x9e\xeb\x9da\xc9R\xcdA\xccD\x1f\t\x99O\x00\xfe\xa9<(W\xb1|\xb3=Rx\xab\xe1\xd3&b\xcd3\x00\x00\x00\x00\x07\x02\x807\x11|\xa3\x80' # noqa: E501
"GENESIS_BLOCK": b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x883|\xde\xeb0\x9e[1:\x97\xe1Jp\x1e\x85|r9 Q\x81*\x058\xe9\xef\xeb\x89\x0c\x8b\xaft\xe6T\xfd\xf2W[\x01\xf0\x16\xc4\n`\xeb\xbd%\x84m\x82\xb5\xc6\xaa\xe8p\x8e\x8f\xa1\x814Rw\xa2\x9b\xce\xd2\x8a\x94\xfc\xb8\x90o\\\xad:;\x12\x9f\xac\xe2f\xe0\x13\x00\xe9]\x83\xa3\xb6\x8f\x15\x8ds\x91\xdd\x12\x00\x00\x00\x90\x9b2K\xbd\x14\xbc\x85/\x06\xad\xc4\xa0%H\xd5\x81\x0b\xe8\x0e\x0c+\xa3W^(\x84e\xb5\xa9I*\xc7L\xb2\x83L\xc4\xca\xe2\xa9\x9a\xc6\xba\xc7\x9b"s\xd0O\xf9\x06&,4\x82tX-I[\xaf\t6Na\x01\x19!;\xbb\x13\x0b\x9bT\x19\x8c\xf6Q2q\x19\taYhL\xc84(\xaa6\xfb\x9aA\xfc\x06\xc7#\xab\xe0\x1c\x843\xf3\xf7k\xd0\xa9\x087N\x1d\x92\x97<o3+\x10J\xf5\xad\xdc\xfb\xa5\x8b[t\xea\xcd\x00uB\xcf\x1e\xf8\xb7\x15\xd6=\x8b;\xc0\xf0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x02\x05\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\r\xc0\x88F\r6\x84\x9d\x85\xc1\xa2\x9aq\xf7\x1cFXm\xf5L\x9f \xbb\x1d\x10\x97\x84\xb5$\x01\xc9^\x7f2\xfb\xee\xeb\x85\xa6\x02\xe3l\xba\xee+\xa25\x00\xd4\xe1\x90/\x08\x97\xef\x18#\xb7\xe5+\xb2\xb9\xc8\xb9\xce\xe1\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x06\xf8\xa8}\xd4_\x98QR\x14"\x06<\xa4;\x9f2\xd3\xed\xd0mK\xf7\xfa~<\x88B\x8b\xd4F\xdcHp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\xc1\xef\x962\x87V\xc4\xd5\xd5AL\x12\x1c\xe4|{\xc0\x7f{.\xe98\x93%\x08:\xa66\xe7\x80Q\x00\x00\x01\xd1\xa9J \x00+[\xbbW\xc5\xdaVh\x93\x16Q\xaa\r\x12\t"\xe3?:fk\xfd\x1f\x82q\xd0\r\x02U\xcc\xf9\xdb\x00\x00\x00\x00\xa3\xaa\xa0\xe0\xediiw+\xae\xecY\r{4\x9f\xc5\x82\'"\x9d\x8dI\x03Y{\x82\xd7pY\xb5\xd7\xc8\x0c-9\xff\xb141\xa5\xee\x17\x01\x1b\x0b&t\xa9\xf6\xe9\xeb\x8a\xc4\x03l\x87\xd0G\x11\x05\xb4\xfap\xe0\x11\xd3W\xcf.\xdb\xc1ye(\xd5A\xc9\x8ds\x83\xaf\xab$\xfd\xf5\x93\xc4,\x7fX\xcf\'\xd7\xa5\xb1\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xb2{\x8c\xa7`a42\xf5\xaa\x87\xa0\rQQB\x1ah*\x07y\xd83k\xf3\xf3\xb6\x8b\x01\xde\xe7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xafw\x92n\x02V\xb3:\x8d\xd4P\x19~\x0b\xa2\x8e\xf5\xaf\'\x1f\xc3\xd8K\xfd\xfd\xc9\x837\x84\x00\xe7K\x06#E\xda\x86B\xf7Q\xb3\xb2\x97\xea\xbd\x96\xdb\xcc\xa1\x91\x87\x04\x1fHqJ\xa2\xbc2\xcc\x04\xacn\x9e\xeb\x9da\xc9R\xcdA\xccD\x1f\t\x99O\x00\xfe\xa9<(W\xb1|\xb3=Rx\xab\xe1\xd3&b\xcd3\x00\x00\x00\x00\x07\x02\x807\x11|\xa3\x80', # noqa: E501
}
)

View File

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

View File

@ -1,10 +1,10 @@
SERVICES_FOR_GROUP = {
"all": "chia_harvester chia_timelord_launcher chia_timelord chia_farmer chia_full_node chia-wallet".split(),
"all": "chia_harvester chia_timelord_launcher chia_timelord chia_farmer chia_full_node chia_wallet".split(),
"node": "chia_full_node".split(),
"harvester": "chia_harvester".split(),
"farmer": "chia_harvester chia_farmer chia_full_node chia-wallet".split(),
"farmer": "chia_harvester chia_farmer chia_full_node chia_wallet".split(),
"timelord": "chia_timelord_launcher chia_timelord chia_full_node".split(),
"wallet-server": "chia-wallet chia_full_node".split(),
"wallet": "chia_wallet chia_full_node".split(),
"introducer": "chia_introducer".split(),
"simulator": "chia_full_node_simulator".split(),
}

View File

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

View File

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

View File

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

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

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 "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):
initial_block_count = 20
reorg_length = 15
blocks = bt.get_consecutive_blocks(
test_constants, initial_block_count, [], 9
)
blocks = bt.get_consecutive_blocks(test_constants, initial_block_count, [], 9)
db_path = Path("blockchain_test.db")
if db_path.exists():
db_path.unlink()

View File

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

View File

@ -111,8 +111,13 @@ class TestRpc:
res_2 = await client_2.get_plots()
assert len(res_2["plots"]) == num_plots
print(await client_2.get_plot_directories())
assert len(await client_2.get_plot_directories()) == 1
await client_2.add_plot_directory(str(plot_dir))
assert len(await client_2.get_plot_directories()) == 2
res_2 = await client_2.get_plots()
assert len(res_2["plots"]) == num_plots + 1
@ -120,6 +125,10 @@ class TestRpc:
res_3 = await client_2.get_plots()
assert len(res_3["plots"]) == num_plots
await client_2.remove_plot_directory(str(plot_dir))
print(await client_2.get_plot_directories())
assert len(await client_2.get_plot_directories()) == 1
except AssertionError:
# Checks that the RPC manages to stop the node
client.close()

View File

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