Cleaner send_transaction flow and more wallet rpc testing
This commit is contained in:
parent
9ed6949099
commit
b791bf1da8
|
@ -25,12 +25,14 @@ proofs of space during testing. The next time tests are run, this won't be neces
|
||||||
```bash
|
```bash
|
||||||
. ./activate
|
. ./activate
|
||||||
pip install -r requirements-dev.txt
|
pip install -r requirements-dev.txt
|
||||||
black src tests && flake8 src --exclude src/wallet/electron/node_modules && mypy src tests
|
black src tests && flake8 src tests && mypy src tests
|
||||||
py.test tests -s -v
|
py.test tests -s -v --durations 0
|
||||||
```
|
```
|
||||||
Black is used as an automatic style formatter to make things easier, and flake8 helps ensure consistent style.
|
Black is used as an automatic style formatter to make things easier, and flake8 helps ensure consistent style.
|
||||||
Mypy is very useful for ensuring objects are of the correct type, so try to always add the type of the return value, and the type of local variables.
|
Mypy is very useful for ensuring objects are of the correct type, so try to always add the type of the return value, and the type of local variables.
|
||||||
|
|
||||||
|
If you want verbose logging for tests, edit the tests/pytest.ini file.
|
||||||
|
|
||||||
## Configure VS code
|
## Configure VS code
|
||||||
1. Install Python extension
|
1. Install Python extension
|
||||||
2. Set the environment to ./venv/bin/python
|
2. Set the environment to ./venv/bin/python
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {
|
import {
|
||||||
get_puzzle_hash,
|
get_address,
|
||||||
format_message,
|
format_message,
|
||||||
incomingMessage,
|
incomingMessage,
|
||||||
get_balance_for_wallet,
|
get_balance_for_wallet,
|
||||||
|
@ -201,7 +201,7 @@ export const handle_message = (store, payload) => {
|
||||||
store.dispatch(get_balance_for_wallet(wallet.id));
|
store.dispatch(get_balance_for_wallet(wallet.id));
|
||||||
store.dispatch(get_transactions(wallet.id));
|
store.dispatch(get_transactions(wallet.id));
|
||||||
if (wallet.type === COLOURED_COIN || wallet.type === STANDARD_WALLET) {
|
if (wallet.type === COLOURED_COIN || wallet.type === STANDARD_WALLET) {
|
||||||
store.dispatch(get_puzzle_hash(wallet.id));
|
store.dispatch(get_address(wallet.id));
|
||||||
}
|
}
|
||||||
if (wallet.type === COLOURED_COIN) {
|
if (wallet.type === COLOURED_COIN) {
|
||||||
store.dispatch(get_colour_name(wallet.id));
|
store.dispatch(get_colour_name(wallet.id));
|
||||||
|
|
|
@ -11,15 +11,16 @@ export const Wallet = (id, name, type, data) => ({
|
||||||
balance_frozen: 0,
|
balance_frozen: 0,
|
||||||
balance_change: 0,
|
balance_change: 0,
|
||||||
transactions: [],
|
transactions: [],
|
||||||
puzzle_hash: "",
|
address: "",
|
||||||
colour: "",
|
colour: "",
|
||||||
|
sending_transaction: false,
|
||||||
send_transaction_result: ""
|
send_transaction_result: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Transaction = (
|
export const Transaction = (
|
||||||
confirmed_at_index,
|
confirmed_at_index,
|
||||||
created_at_time,
|
created_at_time,
|
||||||
to_puzzle_hash,
|
to_address,
|
||||||
amount,
|
amount,
|
||||||
fee_amount,
|
fee_amount,
|
||||||
incoming,
|
incoming,
|
||||||
|
@ -32,7 +33,7 @@ export const Transaction = (
|
||||||
) => ({
|
) => ({
|
||||||
confirmed_at_index: confirmed_at_index,
|
confirmed_at_index: confirmed_at_index,
|
||||||
created_at_time: created_at_time,
|
created_at_time: created_at_time,
|
||||||
to_puzzle_hash: to_puzzle_hash,
|
to_address: to_address,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
fee_amount: fee_amount,
|
fee_amount: fee_amount,
|
||||||
incoming: incoming,
|
incoming: incoming,
|
||||||
|
@ -58,7 +59,6 @@ const initial_state = {
|
||||||
connection_count: 0,
|
connection_count: 0,
|
||||||
syncing: false
|
syncing: false
|
||||||
},
|
},
|
||||||
sending_transaction: false,
|
|
||||||
show_create_backup: false
|
show_create_backup: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,20 +87,24 @@ export const incomingReducer = (state = { ...initial_state }, action) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
case "CLEAR_SEND":
|
case "CLEAR_SEND":
|
||||||
|
id = action.message.data.wallet_id;
|
||||||
|
wallet = state.wallets[parseInt(id)];
|
||||||
|
wallet.sending_transaction = false;
|
||||||
|
wallet.send_transaction_result = null;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
sending_transaction: false,
|
|
||||||
send_transaction_result: null
|
|
||||||
};
|
};
|
||||||
case "OUTGOING_MESSAGE":
|
case "OUTGOING_MESSAGE":
|
||||||
if (
|
if (
|
||||||
action.message.command === "send_transaction" ||
|
action.message.command === "send_transaction" ||
|
||||||
action.message.command === "cc_spend"
|
action.message.command === "cc_spend"
|
||||||
) {
|
) {
|
||||||
|
id = action.message.data.wallet_id;
|
||||||
|
wallet = state.wallets[parseInt(id)];
|
||||||
|
wallet.sending_transaction = false;
|
||||||
|
wallet.send_transaction_result = null;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
sending_transaction: true,
|
|
||||||
send_transaction_result: null
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
@ -190,15 +194,15 @@ export const incomingReducer = (state = { ...initial_state }, action) => {
|
||||||
wallet.transactions = transactions.reverse();
|
wallet.transactions = transactions.reverse();
|
||||||
return { ...state };
|
return { ...state };
|
||||||
}
|
}
|
||||||
} else if (command === "get_next_puzzle_hash") {
|
} else if (command === "get_next_address") {
|
||||||
id = data.wallet_id;
|
id = data.wallet_id;
|
||||||
var puzzle_hash = data.puzzle_hash;
|
var address = data.address;
|
||||||
wallets = state.wallets;
|
wallets = state.wallets;
|
||||||
wallet = wallets[parseInt(id)];
|
wallet = wallets[parseInt(id)];
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
wallet.puzzle_hash = puzzle_hash;
|
wallet.address = address;
|
||||||
return { ...state };
|
return { ...state };
|
||||||
} else if (command === "get_connections") {
|
} else if (command === "get_connections") {
|
||||||
if (data.success || data.connections) {
|
if (data.success || data.connections) {
|
||||||
|
@ -241,12 +245,12 @@ export const incomingReducer = (state = { ...initial_state }, action) => {
|
||||||
wallet.name = name;
|
wallet.name = name;
|
||||||
return { ...state };
|
return { ...state };
|
||||||
}
|
}
|
||||||
if (command === "send_transaction" || command === "cc_spend") {
|
if (command === "tx_update") {
|
||||||
state["sending_transaction"] = false;
|
|
||||||
const id = data.id;
|
const id = data.id;
|
||||||
wallets = state.wallets;
|
wallets = state.wallets;
|
||||||
wallet = wallets[parseInt(id)];
|
wallet = wallets[parseInt(id)];
|
||||||
wallet.send_transaction_result = message.data;
|
wallet.sending_transaction = false;
|
||||||
|
wallet.send_transaction_result = message.data.additional_data;
|
||||||
return { ...state };
|
return { ...state };
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -102,14 +102,18 @@ export const get_balance_for_wallet = id => {
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const send_transaction = (wallet_id, amount, fee, puzzle_hash) => {
|
export const send_transaction = (wallet_id, amount, fee, address) => {
|
||||||
var action = walletMessage();
|
var action = walletMessage();
|
||||||
action.message.command = "send_transaction";
|
action.message.command = "send_transaction";
|
||||||
action.message.data = {
|
action.message.data = {
|
||||||
wallet_id: wallet_id,
|
wallet_id: wallet_id,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
fee: fee,
|
fee: fee,
|
||||||
|
<<<<<<< HEAD
|
||||||
puzzle_hash: puzzle_hash
|
puzzle_hash: puzzle_hash
|
||||||
|
=======
|
||||||
|
address: address,
|
||||||
|
>>>>>>> c125573c... Cleaner send_transaction flow and more wallet rpc testing
|
||||||
};
|
};
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
@ -132,8 +136,29 @@ export const add_key = (mnemonic, type, file_path) => {
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
export const add_new_key_action = mnemonic => {
|
export const add_new_key_action = mnemonic => {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
|
=======
|
||||||
|
export const send_transaction_and_wait = (wallet_id, amount, fee, address) => {
|
||||||
|
return (dispatch) => {
|
||||||
|
try {
|
||||||
|
response = await async_api(dispatch, send_transaction(wallet_id, amount, fee, address), False);
|
||||||
|
if (!response.data.success) {
|
||||||
|
// Do something bad
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Do something good
|
||||||
|
} catch (err) {
|
||||||
|
// Do something bad
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const add_new_key_action = (mnemonic) => {
|
||||||
|
return (dispatch) => {
|
||||||
|
>>>>>>> c125573c... Cleaner send_transaction flow and more wallet rpc testing
|
||||||
return async_api(
|
return async_api(
|
||||||
dispatch,
|
dispatch,
|
||||||
add_key(mnemonic, "new_wallet", null),
|
add_key(mnemonic, "new_wallet", null),
|
||||||
|
@ -388,17 +413,25 @@ export const get_transactions = wallet_id => {
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
export const get_puzzle_hash = wallet_id => {
|
export const get_puzzle_hash = wallet_id => {
|
||||||
|
=======
|
||||||
|
export const get_address = (wallet_id) => {
|
||||||
|
>>>>>>> c125573c... Cleaner send_transaction flow and more wallet rpc testing
|
||||||
var action = walletMessage();
|
var action = walletMessage();
|
||||||
action.message.command = "get_next_puzzle_hash";
|
action.message.command = "get_next_address";
|
||||||
action.message.data = { wallet_id: wallet_id };
|
action.message.data = { wallet_id: wallet_id };
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
export const farm_block = puzzle_hash => {
|
export const farm_block = puzzle_hash => {
|
||||||
|
=======
|
||||||
|
export const farm_block = (address) => {
|
||||||
|
>>>>>>> c125573c... Cleaner send_transaction flow and more wallet rpc testing
|
||||||
var action = walletMessage();
|
var action = walletMessage();
|
||||||
action.message.command = "farm_block";
|
action.message.command = "farm_block";
|
||||||
action.message.data = { puzzle_hash: puzzle_hash };
|
action.message.data = { address: address };
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -536,12 +569,12 @@ export const rename_cc_wallet = (wallet_id, name) => {
|
||||||
return action;
|
return action;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cc_spend = (wallet_id, puzzle_hash, amount, fee) => {
|
export const cc_spend = (wallet_id, address, amount, fee) => {
|
||||||
var action = walletMessage();
|
var action = walletMessage();
|
||||||
action.message.command = "cc_spend";
|
action.message.command = "cc_spend";
|
||||||
action.message.data = {
|
action.message.data = {
|
||||||
wallet_id: wallet_id,
|
wallet_id: wallet_id,
|
||||||
innerpuzhash: puzzle_hash,
|
inner_address: address,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
fee: fee
|
fee: fee
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableHead from "@material-ui/core/TableHead";
|
import TableHead from "@material-ui/core/TableHead";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import {
|
import {
|
||||||
get_puzzle_hash,
|
get_address,
|
||||||
cc_spend,
|
cc_spend,
|
||||||
farm_block,
|
farm_block,
|
||||||
rename_cc_wallet
|
rename_cc_wallet
|
||||||
|
@ -29,6 +29,7 @@ import {
|
||||||
import { unix_to_short_date } from "../util/utils";
|
import { unix_to_short_date } from "../util/utils";
|
||||||
import Accordion from "../components/Accordion";
|
import Accordion from "../components/Accordion";
|
||||||
import { openDialog } from "../modules/dialogReducer";
|
import { openDialog } from "../modules/dialogReducer";
|
||||||
|
import { get_transaction_result } from "../util/transaction_result";
|
||||||
const config = require("../config");
|
const config = require("../config");
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
@ -417,7 +418,7 @@ const SendCard = props => {
|
||||||
const cc_unit = get_cc_unit(name);
|
const cc_unit = get_cc_unit(name);
|
||||||
|
|
||||||
const sending_transaction = useSelector(
|
const sending_transaction = useSelector(
|
||||||
state => state.wallet_state.sending_transaction
|
state => state.wallet_state.wallets[id].sending_transaction
|
||||||
);
|
);
|
||||||
|
|
||||||
const send_transaction_result = useSelector(
|
const send_transaction_result = useSelector(
|
||||||
|
@ -426,21 +427,9 @@ const SendCard = props => {
|
||||||
|
|
||||||
const colour = useSelector(state => state.wallet_state.wallets[id].colour);
|
const colour = useSelector(state => state.wallet_state.wallets[id].colour);
|
||||||
|
|
||||||
let result_message = "";
|
result = get_transaction_result(send_transaction_result);
|
||||||
let result_class = classes.resultSuccess;
|
let result_message = result.message;
|
||||||
if (send_transaction_result) {
|
let result_class = result.success ? classes.resultSuccess : classes.resultFailure;
|
||||||
if (send_transaction_result.status === "SUCCESS") {
|
|
||||||
result_message =
|
|
||||||
"Transaction has successfully been sent to a full node and included in the mempool.";
|
|
||||||
} else if (send_transaction_result.status === "PENDING") {
|
|
||||||
result_message =
|
|
||||||
"Transaction has sent to a full node and is pending inclusion into the mempool. " +
|
|
||||||
send_transaction_result.reason;
|
|
||||||
} else {
|
|
||||||
result_message = "Transaction failed. " + send_transaction_result.reason;
|
|
||||||
result_class = classes.resultFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function farm() {
|
function farm() {
|
||||||
var address = address_input.value;
|
var address = address_input.value;
|
||||||
|
@ -453,7 +442,7 @@ const SendCard = props => {
|
||||||
if (sending_transaction) {
|
if (sending_transaction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let puzzle_hash = address_input.value.trim();
|
let address = address_input.value.trim();
|
||||||
if (
|
if (
|
||||||
amount_input.value === "" ||
|
amount_input.value === "" ||
|
||||||
Number(amount_input.value) === 0 ||
|
Number(amount_input.value) === 0 ||
|
||||||
|
@ -472,8 +461,8 @@ const SendCard = props => {
|
||||||
const fee = colouredcoin_to_mojo(fee_input.value);
|
const fee = colouredcoin_to_mojo(fee_input.value);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
puzzle_hash.includes("chia_addr") ||
|
address.includes("chia_addr") ||
|
||||||
puzzle_hash.includes("colour_desc")
|
address.includes("colour_desc")
|
||||||
) {
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
openDialog(
|
openDialog(
|
||||||
|
@ -482,9 +471,9 @@ const SendCard = props => {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (puzzle_hash.substring(0, 14) === "colour_addr://") {
|
if (address.substring(0, 14) === "colour_addr://") {
|
||||||
const colour_id = puzzle_hash.substring(14, 78);
|
const colour_id = address.substring(14, 78);
|
||||||
puzzle_hash = puzzle_hash.substring(79);
|
address = address.substring(79);
|
||||||
if (colour_id !== colour) {
|
if (colour_id !== colour) {
|
||||||
dispatch(
|
dispatch(
|
||||||
openDialog(
|
openDialog(
|
||||||
|
@ -495,14 +484,14 @@ const SendCard = props => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (puzzle_hash.startsWith("0x") || puzzle_hash.startsWith("0X")) {
|
if (address.startsWith("0x") || address.startsWith("0X")) {
|
||||||
puzzle_hash = puzzle_hash.substring(2);
|
address = address.substring(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount_value = parseFloat(Number(amount));
|
const amount_value = parseFloat(Number(amount));
|
||||||
const fee_value = parseFloat(Number(fee));
|
const fee_value = parseFloat(Number(fee));
|
||||||
|
|
||||||
dispatch(cc_spend(id, puzzle_hash, amount_value, fee_value));
|
dispatch(cc_spend(id, address, amount_value, fee_value));
|
||||||
address_input.value = "";
|
address_input.value = "";
|
||||||
amount_input.value = "";
|
amount_input.value = "";
|
||||||
}
|
}
|
||||||
|
@ -669,7 +658,7 @@ const TransactionTable = props => {
|
||||||
{transactions.map(tx => (
|
{transactions.map(tx => (
|
||||||
<TableRow
|
<TableRow
|
||||||
className={classes.row}
|
className={classes.row}
|
||||||
key={tx.to_puzzle_hash + tx.created_at_time + tx.amount}
|
key={tx.to_address + tx.created_at_time + tx.amount}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.cell_short}>
|
<TableCell className={classes.cell_short}>
|
||||||
{incoming_string(tx.incoming)}
|
{incoming_string(tx.incoming)}
|
||||||
|
@ -678,7 +667,7 @@ const TransactionTable = props => {
|
||||||
style={{ maxWidth: "150px" }}
|
style={{ maxWidth: "150px" }}
|
||||||
className={classes.cell_short}
|
className={classes.cell_short}
|
||||||
>
|
>
|
||||||
{tx.to_puzzle_hash}
|
{tx.to_address}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.cell_short}>
|
<TableCell className={classes.cell_short}>
|
||||||
{unix_to_short_date(tx.created_at_time)}
|
{unix_to_short_date(tx.created_at_time)}
|
||||||
|
@ -702,18 +691,18 @@ const TransactionTable = props => {
|
||||||
|
|
||||||
const AddressCard = props => {
|
const AddressCard = props => {
|
||||||
var id = props.wallet_id;
|
var id = props.wallet_id;
|
||||||
const puzzle_hash = useSelector(
|
const address = useSelector(
|
||||||
state => state.wallet_state.wallets[id].puzzle_hash
|
state => state.wallet_state.wallets[id].address
|
||||||
);
|
);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
function newAddress() {
|
function newAddress() {
|
||||||
dispatch(get_puzzle_hash(id));
|
dispatch(get_address(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy() {
|
function copy() {
|
||||||
navigator.clipboard.writeText(puzzle_hash);
|
navigator.clipboard.writeText(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -734,7 +723,7 @@ const AddressCard = props => {
|
||||||
disabled
|
disabled
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Address"
|
label="Address"
|
||||||
value={puzzle_hash}
|
value={address}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -643,34 +643,22 @@ const SendCard = props => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const sending_transaction = useSelector(
|
const sending_transaction = useSelector(
|
||||||
state => state.wallet_state.sending_transaction
|
state => state.wallet_state.wallets[id].sending_transaction
|
||||||
);
|
);
|
||||||
|
|
||||||
const send_transaction_result = useSelector(
|
const send_transaction_result = useSelector(
|
||||||
state => state.wallet_state.wallets[id].send_transaction_result
|
state => state.wallet_state.wallets[id].send_transaction_result
|
||||||
);
|
);
|
||||||
|
|
||||||
let result_message = "";
|
result = get_transaction_result(send_transaction_result);
|
||||||
let result_class = classes.resultSuccess;
|
let result_message = result.message;
|
||||||
if (send_transaction_result) {
|
let result_class = result.success ? classes.resultSuccess : classes.resultFailure;
|
||||||
if (send_transaction_result.status === "SUCCESS") {
|
|
||||||
result_message =
|
|
||||||
"Transaction has successfully been sent to a full node and included in the mempool.";
|
|
||||||
} else if (send_transaction_result.status === "PENDING") {
|
|
||||||
result_message =
|
|
||||||
"Transaction has sent to a full node and is pending inclusion into the mempool. " +
|
|
||||||
send_transaction_result.reason;
|
|
||||||
} else {
|
|
||||||
result_message = "Transaction failed. " + send_transaction_result.reason;
|
|
||||||
result_class = classes.resultFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function send() {
|
function send() {
|
||||||
if (sending_transaction) {
|
if (sending_transaction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let puzzle_hash = address_input.value.trim();
|
let address = address_input.value.trim();
|
||||||
if (
|
if (
|
||||||
amount_input.value === "" ||
|
amount_input.value === "" ||
|
||||||
Number(amount_input.value) === 0 ||
|
Number(amount_input.value) === 0 ||
|
||||||
|
@ -687,14 +675,14 @@ const SendCard = props => {
|
||||||
const amount = chia_to_mojo(amount_input.value);
|
const amount = chia_to_mojo(amount_input.value);
|
||||||
const fee = chia_to_mojo(fee_input.value);
|
const fee = chia_to_mojo(fee_input.value);
|
||||||
|
|
||||||
if (puzzle_hash.startsWith("0x") || puzzle_hash.startsWith("0X")) {
|
if (address.startsWith("0x") || address.startsWith("0X")) {
|
||||||
puzzle_hash = puzzle_hash.substring(2);
|
address = address.substring(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount_value = parseFloat(Number(amount));
|
const amount_value = parseFloat(Number(amount));
|
||||||
const fee_value = parseFloat(Number(fee));
|
const fee_value = parseFloat(Number(fee));
|
||||||
|
|
||||||
dispatch(send_transaction(id, amount_value, fee_value, puzzle_hash));
|
dispatch(send_transaction(id, amount_value, fee_value, address));
|
||||||
address_input.value = "";
|
address_input.value = "";
|
||||||
amount_input.value = "";
|
amount_input.value = "";
|
||||||
fee_input.value = "";
|
fee_input.value = "";
|
||||||
|
@ -898,7 +886,7 @@ const TransactionTable = props => {
|
||||||
{transactions.map(tx => (
|
{transactions.map(tx => (
|
||||||
<TableRow
|
<TableRow
|
||||||
className={classes.row}
|
className={classes.row}
|
||||||
key={tx.to_puzzle_hash + tx.created_at_time + tx.amount}
|
key={tx.to_address + tx.created_at_time + tx.amount}
|
||||||
>
|
>
|
||||||
<TableCell className={classes.cell_short}>
|
<TableCell className={classes.cell_short}>
|
||||||
{incoming_string(tx.incoming)}
|
{incoming_string(tx.incoming)}
|
||||||
|
@ -907,7 +895,7 @@ const TransactionTable = props => {
|
||||||
style={{ maxWidth: "150px" }}
|
style={{ maxWidth: "150px" }}
|
||||||
className={classes.cell_short}
|
className={classes.cell_short}
|
||||||
>
|
>
|
||||||
{tx.to_puzzle_hash}
|
{tx.to_address}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.cell_short}>
|
<TableCell className={classes.cell_short}>
|
||||||
{unix_to_short_date(tx.created_at_time)}
|
{unix_to_short_date(tx.created_at_time)}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import TableCell from "@material-ui/core/TableCell";
|
||||||
import TableHead from "@material-ui/core/TableHead";
|
import TableHead from "@material-ui/core/TableHead";
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
import {
|
import {
|
||||||
get_puzzle_hash,
|
get_address,
|
||||||
send_transaction,
|
send_transaction,
|
||||||
farm_block
|
farm_block
|
||||||
} from "../modules/message";
|
} from "../modules/message";
|
||||||
|
@ -326,27 +326,16 @@ const SendCard = props => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const sending_transaction = useSelector(
|
const sending_transaction = useSelector(
|
||||||
state => state.wallet_state.sending_transaction
|
state => state.wallet_state.wallets[id].sending_transaction
|
||||||
);
|
);
|
||||||
|
|
||||||
const send_transaction_result = useSelector(
|
const send_transaction_result = useSelector(
|
||||||
state => state.wallet_state.wallets[id].send_transaction_result
|
state => state.wallet_state.wallets[id].send_transaction_result
|
||||||
);
|
);
|
||||||
let result_message = "";
|
|
||||||
let result_class = classes.resultSuccess;
|
result = get_transaction_result(send_transaction_result);
|
||||||
if (send_transaction_result) {
|
let result_message = result.message;
|
||||||
if (send_transaction_result.status === "SUCCESS") {
|
let result_class = result.success ? classes.resultSuccess : classes.resultFailure;
|
||||||
result_message =
|
|
||||||
"Transaction has successfully been sent to a full node and included in the mempool.";
|
|
||||||
} else if (send_transaction_result.status === "PENDING") {
|
|
||||||
result_message =
|
|
||||||
"Transaction has sent to a full node and is pending inclusion into the mempool. " +
|
|
||||||
send_transaction_result.reason;
|
|
||||||
} else {
|
|
||||||
result_message = "Transaction failed. " + send_transaction_result.reason;
|
|
||||||
result_class = classes.resultFailure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function farm() {
|
function farm() {
|
||||||
var address = address_input.value;
|
var address = address_input.value;
|
||||||
|
@ -359,7 +348,7 @@ const SendCard = props => {
|
||||||
if (sending_transaction) {
|
if (sending_transaction) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let puzzle_hash = address_input.value.trim();
|
let address = address_input.value.trim();
|
||||||
if (
|
if (
|
||||||
amount_input.value === "" ||
|
amount_input.value === "" ||
|
||||||
Number(amount_input.value) === 0 ||
|
Number(amount_input.value) === 0 ||
|
||||||
|
@ -376,24 +365,24 @@ const SendCard = props => {
|
||||||
const amount = chia_to_mojo(amount_input.value);
|
const amount = chia_to_mojo(amount_input.value);
|
||||||
const fee = chia_to_mojo(fee_input.value);
|
const fee = chia_to_mojo(fee_input.value);
|
||||||
|
|
||||||
if (puzzle_hash.includes("colour")) {
|
if (address.includes("colour")) {
|
||||||
dispatch(
|
dispatch(
|
||||||
openDialog(
|
openDialog(
|
||||||
"Error: Cannot send chia to coloured address. Please enter a chia address."
|
"Error: Cannot send chia to coloured address. Please enter a chia address."
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (puzzle_hash.substring(0, 12) === "chia_addr://") {
|
} else if (address.substring(0, 12) === "chia_addr://") {
|
||||||
puzzle_hash = puzzle_hash.substring(12);
|
address = address.substring(12);
|
||||||
}
|
}
|
||||||
if (puzzle_hash.startsWith("0x") || puzzle_hash.startsWith("0X")) {
|
if (address.startsWith("0x") || address.startsWith("0X")) {
|
||||||
puzzle_hash = puzzle_hash.substring(2);
|
address = address.substring(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
const amount_value = parseFloat(Number(amount));
|
const amount_value = parseFloat(Number(amount));
|
||||||
const fee_value = parseFloat(Number(fee));
|
const fee_value = parseFloat(Number(fee));
|
||||||
|
|
||||||
dispatch(send_transaction(id, amount_value, fee_value, puzzle_hash));
|
dispatch(send_transaction(id, amount_value, fee_value, address));
|
||||||
address_input.value = "";
|
address_input.value = "";
|
||||||
amount_input.value = "";
|
amount_input.value = "";
|
||||||
fee_input.value = "";
|
fee_input.value = "";
|
||||||
|
@ -565,7 +554,7 @@ const TransactionTable = props => {
|
||||||
<TableRow
|
<TableRow
|
||||||
className={classes.row}
|
className={classes.row}
|
||||||
key={
|
key={
|
||||||
tx.to_puzzle_hash +
|
tx.to_address +
|
||||||
tx.created_at_time +
|
tx.created_at_time +
|
||||||
tx.amount +
|
tx.amount +
|
||||||
(tx.removals.length > 0 ? tx.removals[0].parent_coin_info : "")
|
(tx.removals.length > 0 ? tx.removals[0].parent_coin_info : "")
|
||||||
|
@ -578,7 +567,7 @@ const TransactionTable = props => {
|
||||||
style={{ maxWidth: "150px" }}
|
style={{ maxWidth: "150px" }}
|
||||||
className={classes.cell_short}
|
className={classes.cell_short}
|
||||||
>
|
>
|
||||||
{tx.to_puzzle_hash}
|
{tx.to_address}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className={classes.cell_short}>
|
<TableCell className={classes.cell_short}>
|
||||||
{unix_to_short_date(tx.created_at_time)}
|
{unix_to_short_date(tx.created_at_time)}
|
||||||
|
@ -602,18 +591,18 @@ const TransactionTable = props => {
|
||||||
|
|
||||||
const AddressCard = props => {
|
const AddressCard = props => {
|
||||||
var id = props.wallet_id;
|
var id = props.wallet_id;
|
||||||
const puzzle_hash = useSelector(
|
const address = useSelector(
|
||||||
state => state.wallet_state.wallets[id].puzzle_hash
|
state => state.wallet_state.wallets[id].address
|
||||||
);
|
);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
function newAddress() {
|
function newAddress() {
|
||||||
dispatch(get_puzzle_hash(id));
|
dispatch(get_address(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy() {
|
function copy() {
|
||||||
navigator.clipboard.writeText(puzzle_hash);
|
navigator.clipboard.writeText(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -634,7 +623,7 @@ const AddressCard = props => {
|
||||||
disabled
|
disabled
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Address"
|
label="Address"
|
||||||
value={puzzle_hash}
|
value={address}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
const mempool_inclusion_status = {
|
||||||
|
"SUCCESS": 1, // Transaction added to mempool
|
||||||
|
"PENDING": 2, // Transaction not yet added to mempool
|
||||||
|
"FAILED": 3, // Transaction was invalid and dropped
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_transaction_result(transaction) {
|
||||||
|
let success = true;
|
||||||
|
let message = "The transaction result was received";
|
||||||
|
|
||||||
|
for (let full_node_response of transaction.transaction.sent_to) {
|
||||||
|
console.log("full node response", full_node_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (send_transaction_result) {
|
||||||
|
// if (send_transaction_result.status === "SUCCESS") {
|
||||||
|
// result_message =
|
||||||
|
// "Transaction has successfully been sent to a full node and included in the mempool.";
|
||||||
|
// } else if (send_transaction_result.status === "PENDING") {
|
||||||
|
// result_message =
|
||||||
|
// "Transaction has sent to a full node and is pending inclusion into the mempool. " +
|
||||||
|
// send_transaction_result.reason;
|
||||||
|
// } else {
|
||||||
|
// result_message = "Transaction failed. " + send_transaction_result.reason;
|
||||||
|
// result_class = classes.resultFailure;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
return {
|
||||||
|
message,
|
||||||
|
success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mempool_inclusion_status,
|
||||||
|
get_transaction_result,
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import yaml
|
||||||
|
|
||||||
from src.ssl.create_ssl import generate_selfsigned_cert
|
from src.ssl.create_ssl import generate_selfsigned_cert
|
||||||
from src.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_pool_sk
|
from src.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_pool_sk
|
||||||
|
from src.util.chech32 import encode_puzzle_hash
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser: ArgumentParser):
|
def make_parser(parser: ArgumentParser):
|
||||||
|
@ -54,50 +55,50 @@ def check_keys(new_root):
|
||||||
config: Dict = load_config(new_root, "config.yaml")
|
config: Dict = load_config(new_root, "config.yaml")
|
||||||
pool_child_pubkeys = [master_sk_to_pool_sk(sk).get_g1() for sk, _ in all_sks]
|
pool_child_pubkeys = [master_sk_to_pool_sk(sk).get_g1() for sk, _ in all_sks]
|
||||||
all_targets = []
|
all_targets = []
|
||||||
stop_searching_for_farmer = "xch_target_puzzle_hash" not in config["farmer"]
|
stop_searching_for_farmer = "xch_target_address" not in config["farmer"]
|
||||||
stop_searching_for_pool = "xch_target_puzzle_hash" not in config["pool"]
|
stop_searching_for_pool = "xch_target_address" not in config["pool"]
|
||||||
for i in range(500):
|
for i in range(500):
|
||||||
if stop_searching_for_farmer and stop_searching_for_pool and i > 0:
|
if stop_searching_for_farmer and stop_searching_for_pool and i > 0:
|
||||||
break
|
break
|
||||||
for sk, _ in all_sks:
|
for sk, _ in all_sks:
|
||||||
all_targets.append(
|
all_targets.append(
|
||||||
create_puzzlehash_for_pk(
|
encode_puzzle_hash(create_puzzlehash_for_pk(
|
||||||
master_sk_to_wallet_sk(sk, uint32(i)).get_g1()
|
master_sk_to_wallet_sk(sk, uint32(i)).get_g1()
|
||||||
).hex()
|
))
|
||||||
)
|
)
|
||||||
if all_targets[-1] == config["farmer"].get("xch_target_puzzle_hash"):
|
if all_targets[-1] == config["farmer"].get("xch_target_address"):
|
||||||
stop_searching_for_farmer = True
|
stop_searching_for_farmer = True
|
||||||
if all_targets[-1] == config["pool"].get("xch_target_puzzle_hash"):
|
if all_targets[-1] == config["pool"].get("xch_target_address"):
|
||||||
stop_searching_for_pool = True
|
stop_searching_for_pool = True
|
||||||
|
|
||||||
# Set the destinations
|
# Set the destinations
|
||||||
if "xch_target_puzzle_hash" not in config["farmer"]:
|
if "xch_target_address" not in config["farmer"]:
|
||||||
print(
|
print(
|
||||||
f"Setting the xch destination address for coinbase fees reward to {all_targets[0]}"
|
f"Setting the xch destination address for coinbase fees reward to {all_targets[0]}"
|
||||||
)
|
)
|
||||||
config["farmer"]["xch_target_puzzle_hash"] = all_targets[0]
|
config["farmer"]["xch_target_address"] = all_targets[0]
|
||||||
elif config["farmer"]["xch_target_puzzle_hash"] not in all_targets:
|
elif config["farmer"]["xch_target_address"] not in all_targets:
|
||||||
print(
|
print(
|
||||||
f"WARNING: farmer using a puzzle hash which we don't have the private"
|
f"WARNING: farmer using a puzzle hash which we don't have the private"
|
||||||
f" keys for. Overriding "
|
f" keys for. Overriding "
|
||||||
f"{config['farmer']['xch_target_puzzle_hash']} with {all_targets[0]}"
|
f"{config['farmer']['xch_target_address']} with {all_targets[0]}"
|
||||||
)
|
)
|
||||||
config["farmer"]["xch_target_puzzle_hash"] = all_targets[0]
|
config["farmer"]["xch_target_address"] = all_targets[0]
|
||||||
|
|
||||||
if "pool" not in config:
|
if "pool" not in config:
|
||||||
config["pool"] = {}
|
config["pool"] = {}
|
||||||
if "xch_target_puzzle_hash" not in config["pool"]:
|
if "xch_target_address" not in config["pool"]:
|
||||||
print(
|
print(
|
||||||
f"Setting the xch destination address for coinbase reward to {all_targets[0]}"
|
f"Setting the xch destination address for coinbase reward to {all_targets[0]}"
|
||||||
)
|
)
|
||||||
config["pool"]["xch_target_puzzle_hash"] = all_targets[0]
|
config["pool"]["xch_target_address"] = all_targets[0]
|
||||||
elif config["pool"]["xch_target_puzzle_hash"] not in all_targets:
|
elif config["pool"]["xch_target_address"] not in all_targets:
|
||||||
print(
|
print(
|
||||||
f"WARNING: pool using a puzzle hash which we don't have the private"
|
f"WARNING: pool using a puzzle hash which we don't have the private"
|
||||||
f" keys for. Overriding "
|
f" keys for. Overriding "
|
||||||
f"{config['pool']['xch_target_puzzle_hash']} with {all_targets[0]}"
|
f"{config['pool']['xch_target_address']} with {all_targets[0]}"
|
||||||
)
|
)
|
||||||
config["pool"]["xch_target_puzzle_hash"] = all_targets[0]
|
config["pool"]["xch_target_address"] = all_targets[0]
|
||||||
|
|
||||||
# Set the pool pks in the farmer
|
# Set the pool pks in the farmer
|
||||||
pool_pubkeys_hex = set(bytes(pk).hex() for pk in pool_child_pubkeys)
|
pool_pubkeys_hex = set(bytes(pk).hex() for pk in pool_child_pubkeys)
|
||||||
|
|
|
@ -15,6 +15,7 @@ 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.wallet.util.wallet_types import WalletType
|
from src.wallet.util.wallet_types import WalletType
|
||||||
from src.cmds.units import units
|
from src.cmds.units import units
|
||||||
|
from src.util.chech32 import encode_puzzle_hash
|
||||||
|
|
||||||
|
|
||||||
def make_parser(parser):
|
def make_parser(parser):
|
||||||
|
@ -304,9 +305,9 @@ async def show_async(args, parser):
|
||||||
f"Tx Filter Hash {b'block.transactions_filter'.hex()}\n"
|
f"Tx Filter Hash {b'block.transactions_filter'.hex()}\n"
|
||||||
f"Tx Generator Hash {block.transactions_generator}\n"
|
f"Tx Generator Hash {block.transactions_generator}\n"
|
||||||
f"Coinbase Amount {block.get_coinbase().amount/1000000000000}\n"
|
f"Coinbase Amount {block.get_coinbase().amount/1000000000000}\n"
|
||||||
f"Coinbase Puzzle Hash 0x{block.get_coinbase().puzzle_hash}\n"
|
f"Coinbase Address {encode_puzzle_hash(block.get_coinbase().puzzle_hash)}\n"
|
||||||
f"Fees Amount {block.get_fees_coin().amount/1000000000000}\n"
|
f"Fees Amount {block.get_fees_coin().amount/1000000000000}\n"
|
||||||
f"Fees Puzzle Hash 0x{block.get_fees_coin().puzzle_hash}\n"
|
f"Fees Address {encode_puzzle_hash(block.get_fees_coin().puzzle_hash)}\n"
|
||||||
f"Aggregated Signature {aggregated_signature}"
|
f"Aggregated Signature {aggregated_signature}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -16,6 +16,7 @@ from src.types.pool_target import PoolTarget
|
||||||
from src.util.api_decorators import api_request
|
from src.util.api_decorators import api_request
|
||||||
from src.util.ints import uint32, uint64, uint128, uint8
|
from src.util.ints import uint32, uint64, uint128, uint8
|
||||||
from src.wallet.derive_keys import master_sk_to_farmer_sk, master_sk_to_pool_sk
|
from src.wallet.derive_keys import master_sk_to_farmer_sk, master_sk_to_pool_sk
|
||||||
|
from src.util.chech32 import decode_puzzle_hash
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -57,14 +58,14 @@ class Farmer:
|
||||||
raise RuntimeError(error_str)
|
raise RuntimeError(error_str)
|
||||||
|
|
||||||
# This is the farmer configuration
|
# This is the farmer configuration
|
||||||
self.wallet_target = bytes.fromhex(self.config["xch_target_puzzle_hash"])
|
self.wallet_target = decode_puzzle_hash(bytes.fromhex(self.config["xch_target_address"]))
|
||||||
self.pool_public_keys = [
|
self.pool_public_keys = [
|
||||||
G1Element.from_bytes(bytes.fromhex(pk))
|
G1Element.from_bytes(bytes.fromhex(pk))
|
||||||
for pk in self.config["pool_public_keys"]
|
for pk in self.config["pool_public_keys"]
|
||||||
]
|
]
|
||||||
|
|
||||||
# This is the pool configuration, which should be moved out to the pool once it exists
|
# This is the pool configuration, which should be moved out to the pool once it exists
|
||||||
self.pool_target = bytes.fromhex(pool_config["xch_target_puzzle_hash"])
|
self.pool_target = decode_puzzle_hash(bytes.fromhex(pool_config["xch_target_address"]))
|
||||||
self.pool_sks_map: Dict = {}
|
self.pool_sks_map: Dict = {}
|
||||||
for key in self._get_private_keys():
|
for key in self._get_private_keys():
|
||||||
self.pool_sks_map[bytes(key.get_g1())] = key
|
self.pool_sks_map[bytes(key.get_g1())] = key
|
||||||
|
|
|
@ -560,7 +560,7 @@ class FullNode:
|
||||||
if self.mempool_manager.seen(transaction.transaction_id):
|
if self.mempool_manager.seen(transaction.transaction_id):
|
||||||
return
|
return
|
||||||
|
|
||||||
elif self.mempool_manager.is_fee_enough(transaction.fees, transaction.cost):
|
if self.mempool_manager.is_fee_enough(transaction.fees, transaction.cost):
|
||||||
requestTX = full_node_protocol.RequestTransaction(
|
requestTX = full_node_protocol.RequestTransaction(
|
||||||
transaction.transaction_id
|
transaction.transaction_id
|
||||||
)
|
)
|
||||||
|
@ -613,10 +613,12 @@ class FullNode:
|
||||||
# Ignore if we have already added this transaction
|
# Ignore if we have already added this transaction
|
||||||
if self.mempool_manager.get_spendbundle(tx.transaction.name()) is not None:
|
if self.mempool_manager.get_spendbundle(tx.transaction.name()) is not None:
|
||||||
return
|
return
|
||||||
|
self.log.warning(f"Adding transaction to mempool: {transaction.transaction_id}")
|
||||||
cost, status, error = await self.mempool_manager.add_spendbundle(
|
cost, status, error = await self.mempool_manager.add_spendbundle(
|
||||||
tx.transaction
|
tx.transaction
|
||||||
)
|
)
|
||||||
if status == MempoolInclusionStatus.SUCCESS:
|
if status == MempoolInclusionStatus.SUCCESS:
|
||||||
|
self.log.warning(f"Added transaction to mempool: {transaction.transaction_id}")
|
||||||
fees = tx.transaction.fees()
|
fees = tx.transaction.fees()
|
||||||
assert fees >= 0
|
assert fees >= 0
|
||||||
assert cost is not None
|
assert cost is not None
|
||||||
|
@ -1823,6 +1825,7 @@ class FullNode:
|
||||||
tx.transaction
|
tx.transaction
|
||||||
)
|
)
|
||||||
if status == MempoolInclusionStatus.SUCCESS:
|
if status == MempoolInclusionStatus.SUCCESS:
|
||||||
|
self.log.info(f"Added transaction to mempool: {tx.transaction.name()}")
|
||||||
# Only broadcast successful transactions, not pending ones. Otherwise it's a DOS
|
# Only broadcast successful transactions, not pending ones. Otherwise it's a DOS
|
||||||
# vector.
|
# vector.
|
||||||
fees = tx.transaction.fees()
|
fees = tx.transaction.fees()
|
||||||
|
|
|
@ -45,7 +45,12 @@ class WalletRpcApi:
|
||||||
return {
|
return {
|
||||||
"/get_wallet_balance": self.get_wallet_balance,
|
"/get_wallet_balance": self.get_wallet_balance,
|
||||||
"/send_transaction": self.send_transaction,
|
"/send_transaction": self.send_transaction,
|
||||||
|
<<<<<<< HEAD
|
||||||
"/get_next_puzzle_hash": self.get_next_puzzle_hash,
|
"/get_next_puzzle_hash": self.get_next_puzzle_hash,
|
||||||
|
=======
|
||||||
|
"/get_next_address": self.get_next_address,
|
||||||
|
"/get_transaction": self.get_transaction,
|
||||||
|
>>>>>>> c125573c... Cleaner send_transaction flow and more wallet rpc testing
|
||||||
"/get_transactions": self.get_transactions,
|
"/get_transactions": self.get_transactions,
|
||||||
"/farm_block": self.farm_block,
|
"/farm_block": self.farm_block,
|
||||||
"/get_sync_status": self.get_sync_status,
|
"/get_sync_status": self.get_sync_status,
|
||||||
|
@ -155,18 +160,18 @@ class WalletRpcApi:
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
change = args[0]
|
|
||||||
wallet_id = args[1]
|
|
||||||
data = {
|
data = {
|
||||||
"state": change,
|
"state": args[0],
|
||||||
}
|
}
|
||||||
if wallet_id is not None:
|
if args[1] is not None:
|
||||||
data["wallet_id"] = wallet_id
|
data["wallet_id"] = args[1]
|
||||||
|
if args[2] is not None:
|
||||||
|
data["additional_data"] = args[2]
|
||||||
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_address(self, request: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
Returns a new puzzlehash
|
Returns a new address
|
||||||
"""
|
"""
|
||||||
if self.service is None:
|
if self.service is None:
|
||||||
return {"success": False}
|
return {"success": False}
|
||||||
|
@ -178,10 +183,10 @@ class WalletRpcApi:
|
||||||
|
|
||||||
if wallet.wallet_info.type == WalletType.STANDARD_WALLET.value:
|
if wallet.wallet_info.type == WalletType.STANDARD_WALLET.value:
|
||||||
raw_puzzle_hash = await wallet.get_new_puzzlehash()
|
raw_puzzle_hash = await wallet.get_new_puzzlehash()
|
||||||
puzzle_hash = encode_puzzle_hash(raw_puzzle_hash)
|
address = encode_puzzle_hash(raw_puzzle_hash)
|
||||||
elif wallet.wallet_info.type == WalletType.COLOURED_COIN.value:
|
elif wallet.wallet_info.type == WalletType.COLOURED_COIN.value:
|
||||||
raw_puzzle_hash = await wallet.get_new_inner_hash()
|
raw_puzzle_hash = await wallet.get_new_inner_hash()
|
||||||
puzzle_hash = encode_puzzle_hash(raw_puzzle_hash)
|
address = encode_puzzle_hash(raw_puzzle_hash)
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
|
@ -191,13 +196,12 @@ class WalletRpcApi:
|
||||||
response = {
|
response = {
|
||||||
"success": True,
|
"success": True,
|
||||||
"wallet_id": wallet_id,
|
"wallet_id": wallet_id,
|
||||||
"puzzle_hash": puzzle_hash,
|
"address": address,
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def send_transaction(self, request):
|
async def send_transaction(self, request):
|
||||||
log.debug("rpc_api request: " + str(request))
|
|
||||||
wallet_id = int(request["wallet_id"])
|
wallet_id = int(request["wallet_id"])
|
||||||
wallet = self.service.wallet_state_manager.wallets[wallet_id]
|
wallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||||
try:
|
try:
|
||||||
|
@ -270,7 +274,7 @@ class WalletRpcApi:
|
||||||
|
|
||||||
for tx in transactions:
|
for tx in transactions:
|
||||||
formatted = tx.to_json_dict()
|
formatted = tx.to_json_dict()
|
||||||
formatted["to_puzzle_hash"] = encode_puzzle_hash(tx.to_puzzle_hash)
|
formatted["to_address"] = encode_puzzle_hash(tx.to_address)
|
||||||
formatted_transactions.append(formatted)
|
formatted_transactions.append(formatted)
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
|
@ -462,7 +466,7 @@ class WalletRpcApi:
|
||||||
async def cc_spend(self, request):
|
async def cc_spend(self, request):
|
||||||
wallet_id = int(request["wallet_id"])
|
wallet_id = int(request["wallet_id"])
|
||||||
wallet: CCWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
wallet: CCWallet = self.service.wallet_state_manager.wallets[wallet_id]
|
||||||
encoded_puzzle_hash = request["innerpuzhash"]
|
encoded_puzzle_hash = request["inner_address"]
|
||||||
puzzle_hash = decode_puzzle_hash(encoded_puzzle_hash)
|
puzzle_hash = decode_puzzle_hash(encoded_puzzle_hash)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -475,7 +479,7 @@ class WalletRpcApi:
|
||||||
|
|
||||||
if tx is None:
|
if tx is None:
|
||||||
data = {
|
data = {
|
||||||
"status": "FAILED",
|
"success": False,
|
||||||
"reason": "Failed to generate signed transaction",
|
"reason": "Failed to generate signed transaction",
|
||||||
"id": wallet_id,
|
"id": wallet_id,
|
||||||
}
|
}
|
||||||
|
@ -484,46 +488,17 @@ class WalletRpcApi:
|
||||||
await wallet.wallet_state_manager.add_pending_transaction(tx)
|
await wallet.wallet_state_manager.add_pending_transaction(tx)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
data = {
|
data = {
|
||||||
"status": "FAILED",
|
"success": False,
|
||||||
"reason": f"Failed to push transaction {e}",
|
"reason": f"Failed to push transaction {e}",
|
||||||
|
"id": wallet_id,
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
sent = False
|
return {
|
||||||
start = time.time()
|
"success": True,
|
||||||
while time.time() - start < TIMEOUT:
|
"transaction": tr,
|
||||||
sent_to: List[
|
"transaction_id": tr.spend_bundle.name(),
|
||||||
Tuple[str, MempoolInclusionStatus, Optional[str]]
|
}
|
||||||
] = await self.service.wallet_state_manager.get_transaction_status(
|
|
||||||
tx.name()
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(sent_to) == 0:
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
continue
|
|
||||||
status, err = sent_to[0][1], sent_to[0][2]
|
|
||||||
if status == MempoolInclusionStatus.SUCCESS:
|
|
||||||
data = {"status": "SUCCESS", "id": wallet_id}
|
|
||||||
sent = True
|
|
||||||
break
|
|
||||||
elif status == MempoolInclusionStatus.PENDING:
|
|
||||||
assert err is not None
|
|
||||||
data = {"status": "PENDING", "reason": err, "id": wallet_id}
|
|
||||||
sent = True
|
|
||||||
break
|
|
||||||
elif status == MempoolInclusionStatus.FAILED:
|
|
||||||
assert err is not None
|
|
||||||
data = {"status": "FAILED", "reason": err, "id": wallet_id}
|
|
||||||
sent = True
|
|
||||||
break
|
|
||||||
if not sent:
|
|
||||||
data = {
|
|
||||||
"status": "FAILED",
|
|
||||||
"reason": "Timed out. Transaction may or may not have been sent.",
|
|
||||||
"id": wallet_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
async def cc_get_colour(self, request):
|
async def cc_get_colour(self, request):
|
||||||
wallet_id = int(request["wallet_id"])
|
wallet_id = int(request["wallet_id"])
|
||||||
|
@ -818,13 +793,13 @@ class WalletRpcApi:
|
||||||
tx = await wallet.clawback_rl_coin_transaction()
|
tx = await wallet.clawback_rl_coin_transaction()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
data = {
|
data = {
|
||||||
"status": "FAILED",
|
"success": False,
|
||||||
"reason": f"Failed to generate signed transaction {e}",
|
"reason": f"Failed to generate signed transaction {e}",
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
if tx is None:
|
if tx is None:
|
||||||
data = {
|
data = {
|
||||||
"status": "FAILED",
|
"success": False,
|
||||||
"reason": "Failed to generate signed transaction",
|
"reason": "Failed to generate signed transaction",
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
@ -832,41 +807,14 @@ class WalletRpcApi:
|
||||||
await wallet.push_transaction(tx)
|
await wallet.push_transaction(tx)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
data = {
|
data = {
|
||||||
"status": "FAILED",
|
"success": False,
|
||||||
"reason": f"Failed to push transaction {e}",
|
"reason": f"Failed to push transaction {e}",
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
sent = False
|
|
||||||
start = time.time()
|
|
||||||
while time.time() - start < TIMEOUT:
|
|
||||||
sent_to: List[
|
|
||||||
Tuple[str, MempoolInclusionStatus, Optional[str]]
|
|
||||||
] = await self.service.wallet_state_manager.get_transaction_status(
|
|
||||||
tx.name()
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(sent_to) == 0:
|
# Transaction may not have been included in the mempool yet. Use get_transaction to check.
|
||||||
await asyncio.sleep(1)
|
return {
|
||||||
continue
|
"success": True,
|
||||||
status, err = sent_to[0][1], sent_to[0][2]
|
"transaction": tx,
|
||||||
if status == MempoolInclusionStatus.SUCCESS:
|
"transaction_id": tx.spend_bundle.name(),
|
||||||
data = {"status": "SUCCESS"}
|
}
|
||||||
sent = True
|
|
||||||
break
|
|
||||||
elif status == MempoolInclusionStatus.PENDING:
|
|
||||||
assert err is not None
|
|
||||||
data = {"status": "PENDING", "reason": err}
|
|
||||||
sent = True
|
|
||||||
break
|
|
||||||
elif status == MempoolInclusionStatus.FAILED:
|
|
||||||
assert err is not None
|
|
||||||
data = {"status": "FAILED", "reason": err}
|
|
||||||
sent = True
|
|
||||||
break
|
|
||||||
if not sent:
|
|
||||||
data = {
|
|
||||||
"status": "FAILED",
|
|
||||||
"reason": "Timed out. Transaction may or may not have been sent.",
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
|
@ -36,6 +36,9 @@ class WalletRpcClient(RpcClient):
|
||||||
return TransactionRecord.from_json_dict(response["transaction"])
|
return TransactionRecord.from_json_dict(response["transaction"])
|
||||||
raise Exception(response["reason"])
|
raise Exception(response["reason"])
|
||||||
|
|
||||||
|
async def get_next_address(self, wallet_id: str) -> Dict:
|
||||||
|
return await self.fetch("get_next_address", {"wallet_id": wallet_id})
|
||||||
|
|
||||||
async def get_transaction(
|
async def get_transaction(
|
||||||
self, wallet_id: str, transaction_id: bytes32
|
self, wallet_id: str, transaction_id: bytes32
|
||||||
) -> Optional[TransactionRecord]:
|
) -> Optional[TransactionRecord]:
|
||||||
|
|
|
@ -32,7 +32,7 @@ harvester:
|
||||||
|
|
||||||
pool: {
|
pool: {
|
||||||
# Replace this with a real puzzle hash
|
# Replace this with a real puzzle hash
|
||||||
# xch_target_puzzle_hash: a4259182b4d8e0af21331fc5be2681f953400b6726fa4095e3b91ae8f005a836
|
# xch_target_address: txch102gkhhzs60grx7cfnpng5n6rjecr89r86l5s8xux2za8k820cxsq64ssdg
|
||||||
logging: *logging
|
logging: *logging
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ farmer:
|
||||||
pool_public_keys: []
|
pool_public_keys: []
|
||||||
|
|
||||||
# Replace this with a real puzzle hash
|
# Replace this with a real puzzle hash
|
||||||
# xch_target_puzzle_hash: a4259182b4d8e0af21331fc5be2681f953400b6726fa4095e3b91ae8f005a836
|
# xch_target_address: txch102gkhhzs60grx7cfnpng5n6rjecr89r86l5s8xux2za8k820cxsq64ssdg
|
||||||
|
|
||||||
# If True, starts an RPC server at the following port
|
# If True, starts an RPC server at the following port
|
||||||
start_rpc_server: True
|
start_rpc_server: True
|
||||||
|
|
|
@ -319,7 +319,7 @@ class WalletStateManager:
|
||||||
continue
|
continue
|
||||||
puzzlehash: bytes32 = puzzle.get_tree_hash()
|
puzzlehash: bytes32 = puzzle.get_tree_hash()
|
||||||
self.log.info(
|
self.log.info(
|
||||||
f"Puzzle at index {index} wid {wallet_id} puzzle hash {puzzlehash.hex()}"
|
f"Puzzle at index {index} with {wallet_id} puzzle hash {puzzlehash.hex()}"
|
||||||
)
|
)
|
||||||
derivation_paths.append(
|
derivation_paths.append(
|
||||||
DerivationRecord(
|
DerivationRecord(
|
||||||
|
@ -376,13 +376,13 @@ class WalletStateManager:
|
||||||
"""
|
"""
|
||||||
self.pending_tx_callback = callback
|
self.pending_tx_callback = callback
|
||||||
|
|
||||||
def state_changed(self, state: str, wallet_id: int = None):
|
def state_changed(self, state: str, wallet_id: int = None, data_object = {}):
|
||||||
"""
|
"""
|
||||||
Calls the callback if it's present.
|
Calls the callback if it's present.
|
||||||
"""
|
"""
|
||||||
if self.state_changed_callback is None:
|
if self.state_changed_callback is None:
|
||||||
return
|
return
|
||||||
self.state_changed_callback(state, wallet_id)
|
self.state_changed_callback(state, wallet_id, data_object)
|
||||||
|
|
||||||
def tx_pending_changed(self):
|
def tx_pending_changed(self):
|
||||||
"""
|
"""
|
||||||
|
@ -698,7 +698,9 @@ class WalletStateManager:
|
||||||
Full node received our transaction, no need to keep it in queue anymore
|
Full node received our transaction, no need to keep it in queue anymore
|
||||||
"""
|
"""
|
||||||
await self.tx_store.increment_sent(spendbundle_id, name, send_status, error)
|
await self.tx_store.increment_sent(spendbundle_id, name, send_status, error)
|
||||||
self.state_changed("tx_sent")
|
tx: Optional[TransactionRecord] = self.get_transaction(spendbundle_id)
|
||||||
|
if tx is not None:
|
||||||
|
self.state_changed("tx_update", tx.wallet_id, {"transaction": tx})
|
||||||
|
|
||||||
async def get_send_queue(self) -> List[TransactionRecord]:
|
async def get_send_queue(self) -> List[TransactionRecord]:
|
||||||
"""
|
"""
|
||||||
|
@ -1541,13 +1543,3 @@ class WalletStateManager:
|
||||||
if callback_str is not None:
|
if callback_str is not None:
|
||||||
callback = getattr(wallet, callback_str)
|
callback = getattr(wallet, callback_str)
|
||||||
await callback(height, header_hash, program, action.id)
|
await callback(height, header_hash, program, action.id)
|
||||||
|
|
||||||
async def get_transaction_status(
|
|
||||||
self, tx_id: bytes32
|
|
||||||
) -> List[Tuple[str, MempoolInclusionStatus, Optional[str]]]:
|
|
||||||
tr: Optional[TransactionRecord] = await self.get_transaction(tx_id)
|
|
||||||
ret_list = []
|
|
||||||
if tr is not None:
|
|
||||||
for (name, ss, err) in tr.sent_to:
|
|
||||||
ret_list.append((name, MempoolInclusionStatus(ss), err))
|
|
||||||
return ret_list
|
|
||||||
|
|
|
@ -40,8 +40,9 @@ class TestWalletRpc:
|
||||||
wallet_node, server_2 = wallets[0]
|
wallet_node, server_2 = wallets[0]
|
||||||
wallet_node_2, server_3 = wallets[1]
|
wallet_node_2, server_3 = wallets[1]
|
||||||
wallet = wallet_node.wallet_state_manager.main_wallet
|
wallet = wallet_node.wallet_state_manager.main_wallet
|
||||||
|
wallet_2 = wallet_node_2.wallet_state_manager.main_wallet
|
||||||
ph = await wallet.get_new_puzzlehash()
|
ph = await wallet.get_new_puzzlehash()
|
||||||
ph_2 = await wallet.get_new_puzzlehash()
|
ph_2 = await wallet_2.get_new_puzzlehash()
|
||||||
|
|
||||||
await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None)
|
await server_2.start_client(PeerInfo("localhost", uint16(server_1._port)), None)
|
||||||
|
|
||||||
|
@ -54,6 +55,12 @@ class TestWalletRpc:
|
||||||
for i in range(1, num_blocks - 1)
|
for i in range(1, num_blocks - 1)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
initial_funds_eventually = sum(
|
||||||
|
[
|
||||||
|
calculate_base_fee(uint32(i)) + calculate_block_reward(uint32(i))
|
||||||
|
for i in range(1, num_blocks + 1)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
wallet_rpc_api = WalletRpcApi(wallet_node)
|
wallet_rpc_api = WalletRpcApi(wallet_node)
|
||||||
|
|
||||||
|
@ -81,27 +88,29 @@ class TestWalletRpc:
|
||||||
addr = encode_puzzle_hash(
|
addr = encode_puzzle_hash(
|
||||||
await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash()
|
await wallet_node_2.wallet_state_manager.main_wallet.get_new_puzzlehash()
|
||||||
)
|
)
|
||||||
tx = await client.send_transaction("1", 1, addr)
|
tx_amount = 15600000
|
||||||
|
tx = await client.send_transaction("1", tx_amount, addr)
|
||||||
assert tx is not None
|
assert tx is not None
|
||||||
transaction_id = tx.name()
|
transaction_id = tx.name()
|
||||||
|
|
||||||
async def tx_in_mempool():
|
async def tx_in_mempool():
|
||||||
tx = await client.get_transaction("1", transaction_id)
|
tx = await client.get_transaction("1", transaction_id)
|
||||||
return tx.is_in_mempool()
|
return tx.is_in_mempool()
|
||||||
|
|
||||||
await time_out_assert(5, tx_in_mempool, True)
|
await time_out_assert(5, tx_in_mempool, True)
|
||||||
await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds - 1)
|
await time_out_assert(5, wallet.get_unconfirmed_balance, initial_funds - tx_amount)
|
||||||
assert (await client.get_wallet_balance("1"))["wallet_balance"]["unconfirmed_wallet_balance"] == initial_funds - 1
|
assert (await client.get_wallet_balance("1"))["wallet_balance"]["unconfirmed_wallet_balance"] == initial_funds - tx_amount
|
||||||
assert (await client.get_wallet_balance("1"))["wallet_balance"]["confirmed_wallet_balance"] == initial_funds
|
assert (await client.get_wallet_balance("1"))["wallet_balance"]["confirmed_wallet_balance"] == initial_funds
|
||||||
|
|
||||||
for i in range(0, num_blocks * 5):
|
for i in range(0, 5):
|
||||||
await full_node_1.farm_new_block(FarmNewBlockProtocol(ph_2))
|
await full_node_1.farm_new_block(FarmNewBlockProtocol(ph_2))
|
||||||
|
|
||||||
assert (await client.get_wallet_balance("1"))["wallet_balance"]["confirmed_wallet_balance"] == initial_funds - 1
|
async def eventual_balance():
|
||||||
|
return (await client.get_wallet_balance("1"))["wallet_balance"]["confirmed_wallet_balance"]
|
||||||
|
|
||||||
|
await time_out_assert(5, eventual_balance, initial_funds_eventually - tx_amount)
|
||||||
|
|
||||||
|
await client.get_next_address()
|
||||||
|
|
||||||
print(await client.get_wallet_balance("1"))
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Checks that the RPC manages to stop the node
|
# Checks that the RPC manages to stop the node
|
||||||
|
|
|
@ -21,6 +21,7 @@ from src.util.ints import uint16, uint32
|
||||||
from src.server.start_service import Service
|
from src.server.start_service import Service
|
||||||
from src.util.make_test_constants import make_test_constants_with_genesis
|
from src.util.make_test_constants import make_test_constants_with_genesis
|
||||||
from tests.time_out_assert import time_out_assert
|
from tests.time_out_assert import time_out_assert
|
||||||
|
from src.util.chech32 import encode_puzzle_hash
|
||||||
|
|
||||||
|
|
||||||
test_constants, bt = make_test_constants_with_genesis(
|
test_constants, bt = make_test_constants_with_genesis(
|
||||||
|
@ -269,9 +270,9 @@ async def setup_farmer(
|
||||||
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")
|
||||||
|
|
||||||
config["xch_target_puzzle_hash"] = bt.farmer_ph.hex()
|
config["xch_target_address"] = encode_puzzle_hash(bt.farmer_ph).hex()
|
||||||
config["pool_public_keys"] = [bytes(pk).hex() for pk in bt.pool_pubkeys]
|
config["pool_public_keys"] = [bytes(pk).hex() for pk in bt.pool_pubkeys]
|
||||||
config_pool["xch_target_puzzle_hash"] = bt.pool_ph.hex()
|
config_pool["xch_target_address"] = encode_puzzle_hash(bt.pool_ph).hex()
|
||||||
if full_node_port:
|
if full_node_port:
|
||||||
connect_peers = [PeerInfo(self_hostname, full_node_port)]
|
connect_peers = [PeerInfo(self_hostname, full_node_port)]
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue