signpost - unfinished just parking changes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import {MixNodeBond, PagedMixnodeResponse} from "../types";
|
||||
import { INetClient } from "../net-client"
|
||||
import {IQueryClient} from "../query-client";
|
||||
import {VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT} from "../index";
|
||||
import { MixNodeBond, PagedMixnodeResponse } from "../types";
|
||||
import { INetClient } from "../net-client";
|
||||
import { IQueryClient } from "../query-client";
|
||||
import { VALIDATOR_API_MIXNODES, VALIDATOR_API_PORT } from "../index";
|
||||
import axios from "axios";
|
||||
|
||||
export { MixnodesCache };
|
||||
@@ -13,48 +13,52 @@ export { MixnodesCache };
|
||||
* available for querying.
|
||||
* */
|
||||
export default class MixnodesCache {
|
||||
mixNodes: MixNodeBond[]
|
||||
client: INetClient | IQueryClient
|
||||
perPage: number
|
||||
mixNodes: MixNodeBond[];
|
||||
client: INetClient | IQueryClient;
|
||||
perPage: number;
|
||||
|
||||
constructor(client: INetClient | IQueryClient, perPage: number) {
|
||||
this.client = client;
|
||||
this.mixNodes = [];
|
||||
this.perPage = perPage;
|
||||
constructor(client: INetClient | IQueryClient, perPage: number) {
|
||||
this.client = client;
|
||||
this.mixNodes = [];
|
||||
this.perPage = perPage;
|
||||
}
|
||||
|
||||
/// Makes repeated requests to assemble a full list of nodes.
|
||||
/// Requests continue to be make as long as `shouldMakeAnotherRequest()`
|
||||
// returns true.
|
||||
async refreshMixNodes(contractAddress: string): Promise<MixNodeBond[]> {
|
||||
let newMixnodes: MixNodeBond[] = [];
|
||||
let response: PagedMixnodeResponse;
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
response = await this.client.getMixNodes(
|
||||
contractAddress,
|
||||
this.perPage,
|
||||
next
|
||||
);
|
||||
newMixnodes = newMixnodes.concat(response.nodes);
|
||||
next = response.start_next_after;
|
||||
// if `start_next_after` is not set, we're done
|
||||
if (!next) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes repeated requests to assemble a full list of nodes.
|
||||
/// Requests continue to be make as long as `shouldMakeAnotherRequest()`
|
||||
// returns true.
|
||||
async refreshMixNodes(contractAddress: string): Promise<MixNodeBond[]> {
|
||||
let newMixnodes: MixNodeBond[] = [];
|
||||
let response: PagedMixnodeResponse;
|
||||
let next: string | undefined = undefined;
|
||||
for (;;) {
|
||||
response = await this.client.getMixNodes(contractAddress, this.perPage, next);
|
||||
newMixnodes = newMixnodes.concat(response.nodes)
|
||||
next = response.start_next_after;
|
||||
// if `start_next_after` is not set, we're done
|
||||
if (!next) {
|
||||
break
|
||||
}
|
||||
}
|
||||
this.mixNodes = newMixnodes;
|
||||
return this.mixNodes;
|
||||
}
|
||||
|
||||
this.mixNodes = newMixnodes
|
||||
return this.mixNodes;
|
||||
/// Makes requests to assemble a full list of mixnodes from validator-api
|
||||
async refreshValidatorAPIMixNodes(urls: string[]): Promise<MixNodeBond[]> {
|
||||
for (const url of urls) {
|
||||
const validator_api_url = new URL(url);
|
||||
validator_api_url.port = VALIDATOR_API_PORT;
|
||||
validator_api_url.pathname += VALIDATOR_API_MIXNODES;
|
||||
const response = await axios.get(validator_api_url.toString());
|
||||
if (response.status == 200) {
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes requests to assemble a full list of mixnodes from validator-api
|
||||
async refreshValidatorAPIMixNodes(urls: string[]): Promise<MixNodeBond[]> {
|
||||
for (const url of urls) {
|
||||
const validator_api_url = new URL(url);
|
||||
validator_api_url.port = VALIDATOR_API_PORT;
|
||||
validator_api_url.pathname += VALIDATOR_API_MIXNODES;
|
||||
const response = await axios.get(validator_api_url.toString());
|
||||
if (response.status == 200) {
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
throw new Error("None of the provided validators seem to be alive")
|
||||
}
|
||||
}
|
||||
throw new Error("None of the provided validators seem to be alive");
|
||||
}
|
||||
}
|
||||
|
||||
+189
-14
@@ -4,15 +4,28 @@ import inquirer from "inquirer";
|
||||
|
||||
const VALIDATOR_URLS: string[] = [
|
||||
"https://testnet-milhon-validator1.nymtech.net",
|
||||
"https://testnet-milhon-validator2.nymtech.net",
|
||||
// "https://testnet-milhon-validator2.nymtech.net", // <-- val 2 doesnt work apparently.
|
||||
];
|
||||
const DENOM = "punk";
|
||||
const MOCK_MNEMONIC =
|
||||
"vault risk throw flat garlic pretty clay senior birth correct panic floor around pen horror mail entry arrest zoo devote message evoke street total";
|
||||
// ^^ addr: punk10dxwmqjy72s9nkm9x9pluyn6pyx0gkptjhs4k9
|
||||
// curr balance: 899999747
|
||||
|
||||
// const MOCK_MNEMONIC =
|
||||
// "oil once motion cute crawl patch happy wave donkey zoo retreat matrix emerge adult very universe aware error snap credit actress couple upset engine";
|
||||
// ^^ addr: punk1yzr7gtmtlfd0s7s9wpexhteeu05y4xlcvh65eh
|
||||
// curr balance: 5045 UPUNK
|
||||
|
||||
// const MOCK_MNEMONIC =
|
||||
// "sample menu edit midnight guard review call record horn antenna stairs awkward fringe document during amazing twelve wise wide escape matter betray staff someone";
|
||||
// ^^ addr: punk1wn8lwxe5hvdtx60c6p7ekskmu75agwfrslf0qs
|
||||
// curr balance:
|
||||
|
||||
type AccountType = {
|
||||
addr: string;
|
||||
client: any;
|
||||
mnemonic: string;
|
||||
mnemonic?: string;
|
||||
};
|
||||
function validatorCli() {
|
||||
// define funcs to be used in CLI switch-case
|
||||
@@ -43,6 +56,75 @@ function validatorCli() {
|
||||
restartApp();
|
||||
}
|
||||
|
||||
function sendFundsMenu() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
name: "recipient",
|
||||
type: "input",
|
||||
message: "please enter the receipient:",
|
||||
},
|
||||
{
|
||||
name: "amount",
|
||||
type: "input",
|
||||
message: "please enter the amount (UPUNK):",
|
||||
},
|
||||
])
|
||||
.then(async ({ recipient, amount }) => {
|
||||
const { addr, client } = state;
|
||||
console.log(
|
||||
`🔥 Hold Tight - Sending ${amount}UPUNK to ${recipient} 🚀`
|
||||
);
|
||||
|
||||
const res = await client.send(addr, recipient, [
|
||||
{
|
||||
denom: "upunk",
|
||||
amount: amount,
|
||||
},
|
||||
]);
|
||||
console.log("Funds Transfer Response:", res);
|
||||
restartApp();
|
||||
});
|
||||
}
|
||||
|
||||
async function delegateGateway() {
|
||||
console.log(
|
||||
"unfortunately - gateway delegation is switched off at the moment."
|
||||
);
|
||||
startTransactionMenu();
|
||||
// const id = "punk1yzr7gtmtlfd0s7s9wpexhteeu05y4xlcvh65eh";
|
||||
// const gatewayID = "EQhjPpUuy4i1u87nfQMW21WiBT5mJk4dcq4ju7Vct7cB";
|
||||
// const coin = {
|
||||
// denom: "upunk",
|
||||
// amount: "101",
|
||||
// };
|
||||
// const res = await state.client.delegateToMixnode(gatewayID, coin);
|
||||
// console.log("delegateMixnode ==> ", res);
|
||||
}
|
||||
|
||||
async function delegateMixnode() {
|
||||
const mixNodeID = "2cFpCe7yP79CcuRpf6JBRdJaSp7JF5YcA5SHi8JVm1d2";
|
||||
// const mixNodeID = "2Vrr7s2peGiWsPh6xY3ZFEMDRmMNv8xLBUtV5XMyQLSB";
|
||||
const coin = {
|
||||
denom: "upunk",
|
||||
amount: "1001",
|
||||
};
|
||||
const res = await state.client.delegateToMixnode(mixNodeID, coin);
|
||||
console.log("delegate to mixnode response: ", res);
|
||||
}
|
||||
async function findMinimumMixnodeBond() {
|
||||
const res = await state.client.minimumMixnodeBond();
|
||||
console.log("res is back ", res);
|
||||
}
|
||||
|
||||
async function bondMixnode() {
|
||||
state.client.bondMixnode();
|
||||
}
|
||||
|
||||
async function checkOwnsMixnodes() {
|
||||
const res = await state.client.ownsMixNode();
|
||||
console.log("owns mixnode? ", res);
|
||||
}
|
||||
function startTransactionMenu() {
|
||||
inquirer
|
||||
.prompt([
|
||||
@@ -50,19 +132,88 @@ function validatorCli() {
|
||||
type: "list",
|
||||
name: "task",
|
||||
message: "What now?",
|
||||
choices: ["send_funds"],
|
||||
choices: [
|
||||
"send_funds",
|
||||
"get_mixnodes",
|
||||
"refresh_mixnodes",
|
||||
"refresh_val_api_mixnodes",
|
||||
"min_mixn_bond",
|
||||
"bond_mixnode",
|
||||
"delegate_mixnode",
|
||||
"delegate_gateway",
|
||||
"check_owns_mixnode",
|
||||
],
|
||||
},
|
||||
])
|
||||
.then(({ task }) => {
|
||||
switch (task) {
|
||||
case "send_funds":
|
||||
console.log("sending funds from ", state.addr);
|
||||
sendFundsMenu();
|
||||
break;
|
||||
case "get_mixnodes":
|
||||
getMixnodes();
|
||||
break;
|
||||
case "refresh_mixnodes":
|
||||
refreshMixnodes();
|
||||
break;
|
||||
case "refresh_val_api_mixnodes":
|
||||
refreshValApiMixnodes();
|
||||
break;
|
||||
case "min_mixn_bond":
|
||||
findMinimumMixnodeBond();
|
||||
break;
|
||||
case "bond_mixnode":
|
||||
bondMixnode();
|
||||
break;
|
||||
case "delegate_gateway":
|
||||
delegateGateway();
|
||||
break;
|
||||
case "delegate_mixnode":
|
||||
delegateMixnode();
|
||||
break;
|
||||
case "check_owns_mixnode":
|
||||
checkOwnsMixnodes();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function queryUserAccount() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "input",
|
||||
name: "query_user",
|
||||
message: "Please enter the public address of user you wish to query",
|
||||
},
|
||||
])
|
||||
.then(async ({ query_user }) => {
|
||||
let response = "";
|
||||
try {
|
||||
const client = await ValidatorClient.connectForQuery(
|
||||
query_user,
|
||||
VALIDATOR_URLS,
|
||||
DENOM
|
||||
);
|
||||
const balance = await client.getBalance(query_user);
|
||||
response = `User ${query_user} has a balance of ${balance?.amount}${balance?.denom}`;
|
||||
console.log(response);
|
||||
return validatorCli();
|
||||
} catch (error) {
|
||||
console.log("error back ", error);
|
||||
return validatorCli();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshMixnodes() {
|
||||
const res = await state.client.refreshMixNodes(
|
||||
"punk1yksauczytk60x5cejaras8w6nwf7r772n3kwkp"
|
||||
);
|
||||
console.log("done:", res);
|
||||
}
|
||||
function connectAccount() {
|
||||
inquirer
|
||||
.prompt([
|
||||
@@ -80,7 +231,6 @@ function validatorCli() {
|
||||
"punk"
|
||||
);
|
||||
|
||||
console.log("Decryped address:", addr);
|
||||
const client = await ValidatorClient.connect(
|
||||
addr,
|
||||
MOCK_MNEMONIC,
|
||||
@@ -97,8 +247,9 @@ function validatorCli() {
|
||||
const balance = await client.getBalance(addr);
|
||||
console.log(`connected to validator, our address is ${client.address}`);
|
||||
console.log("connected to validator", client.urls[0]);
|
||||
console.log("📫 Your address:", addr);
|
||||
console.log("💰 Your balance:", balance);
|
||||
console.log(
|
||||
`💰 Your balance is ${balance?.amount}${balance?.denom.toUpperCase()}`
|
||||
);
|
||||
|
||||
startTransactionMenu();
|
||||
})
|
||||
@@ -106,19 +257,40 @@ function validatorCli() {
|
||||
console.log("error: ", err);
|
||||
});
|
||||
}
|
||||
|
||||
function buildAWallet() {
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
message: "enter your mnemonic to build wallet:",
|
||||
type: "input",
|
||||
name: "mnemonic",
|
||||
},
|
||||
])
|
||||
.then(async ({ mnemonic }) => {
|
||||
const res = await ValidatorClient.buildWallet(mnemonic, DENOM);
|
||||
console.log("Build_Wallet Response: ", res);
|
||||
});
|
||||
}
|
||||
async function refreshValApiMixnodes() {
|
||||
const res = await state.client.refreshValidatorAPIMixNodes();
|
||||
console.log("res is back: ", res);
|
||||
}
|
||||
function getMixnodes() {
|
||||
const res = state.client.mixNodesCache;
|
||||
console.log("Mixnodes", res);
|
||||
}
|
||||
// app provides a list of possible tasks
|
||||
inquirer
|
||||
.prompt([
|
||||
{
|
||||
type: "list",
|
||||
name: "task",
|
||||
message: "So...What would you like to do today?",
|
||||
message: "Yo, What would you like to do today?",
|
||||
choices: [
|
||||
"create_account",
|
||||
"connect_account",
|
||||
"get_mixnodes",
|
||||
"send_funds",
|
||||
"build_wallet",
|
||||
"query_user",
|
||||
],
|
||||
},
|
||||
])
|
||||
@@ -130,6 +302,12 @@ function validatorCli() {
|
||||
case "connect_account":
|
||||
connectAccount();
|
||||
break;
|
||||
case "build_wallet":
|
||||
buildAWallet();
|
||||
break;
|
||||
case "query_user":
|
||||
queryUserAccount();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -137,6 +315,3 @@ function validatorCli() {
|
||||
}
|
||||
|
||||
validatorCli();
|
||||
// if it's get mixnodes, return all mixnodes
|
||||
// if it's create an account
|
||||
// if it's send funds from one address to another
|
||||
|
||||
@@ -80,6 +80,13 @@ export default class ValidatorClient {
|
||||
this.denom = "u" + prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contractAddress the user's contract address eg. `punk23o85698370891702470413487`
|
||||
* @param mnemonic A mnemonic string from which to generate a public/private keypair.
|
||||
* @param urls the validator URLs in either array of string format.
|
||||
* @param prefix the denom eg. `punk`
|
||||
* @returns user's instance of the Validator Client.
|
||||
*/
|
||||
// allows also entering 'string' by itself for backwards compatibility
|
||||
static async connect(
|
||||
contractAddress: string,
|
||||
@@ -126,6 +133,14 @@ export default class ValidatorClient {
|
||||
throw new Error("None of the provided validators seem to be alive");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is the same as connect() but doesnt require a mnemonic
|
||||
* as you cannot transfer/withdraw once connected. It is effectively ReadOnly.
|
||||
* @param contractAddress the user's contract address eg. `punk23o85698370891702470413487`
|
||||
* @param urls the validator URLs in either array of string format.
|
||||
* @param prefix the denom eg. `punk`
|
||||
* @returns user's instance of the Validator Client.
|
||||
*/
|
||||
// allows also entering 'string' by itself for backwards compatibility
|
||||
static async connectForQuery(
|
||||
contractAddress: string,
|
||||
@@ -164,6 +179,10 @@ export default class ValidatorClient {
|
||||
throw new Error("None of the provided validators seem to be alive");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urls the validator URLs in either array of string format.
|
||||
* @returns a shuffled array of validator URLs.
|
||||
*/
|
||||
private static ensureArray(urls: string | string[]): string[] {
|
||||
let validatorsUrls: string[] = [];
|
||||
if (typeof urls === "string") {
|
||||
@@ -184,8 +203,12 @@ export default class ValidatorClient {
|
||||
return validatorsUrls;
|
||||
}
|
||||
|
||||
// an error adapter function that upon an error attempts to switch currently used validator to the next one available
|
||||
// note that it ALWAYS throws an error
|
||||
/**
|
||||
* Error adapter function that - upon an error - attempts to switch currently used validator to the next one available
|
||||
* note that it ALWAYS throws an error
|
||||
* @param error Error thrown by async/netw requests in other methods within this class.
|
||||
* @returns a thrown error, as normal.
|
||||
*/
|
||||
async handleRequestFailure(error: Error): Promise<never> {
|
||||
// don't bother doing any fancy validator switches if we only have 1 validator to choose from
|
||||
if (this.urls.length > 1) {
|
||||
@@ -208,12 +231,21 @@ export default class ValidatorClient {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* changes the client's validator to the new validator passed in.
|
||||
* @param newUrl the URL of the new/alternative validator.
|
||||
* @returns void
|
||||
*/
|
||||
private async changeValidator(newUrl: string): Promise<void> {
|
||||
console.log("Changing validator to", newUrl);
|
||||
return await this.client.changeValidator(newUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* shuffles the array
|
||||
* @param arr the URL of the new/alternative validator.
|
||||
* @returns array
|
||||
*/
|
||||
// adapted from https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array/6274381#6274381
|
||||
static shuffleArray<T>(arr: T[]): T[] {
|
||||
for (let i = arr.length - 1; i > 0; i--) {
|
||||
@@ -223,6 +255,12 @@ export default class ValidatorClient {
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* utility function that moves the first element (Val url) to the back
|
||||
* of the array again.
|
||||
* @param arr
|
||||
* @returns void
|
||||
*/
|
||||
// It is responsibility of the caller to ensure the input array is non-empty
|
||||
private static moveArrayHeadToBack<T>(arr: T[]) {
|
||||
const head = <T>arr.shift();
|
||||
@@ -265,6 +303,7 @@ export default class ValidatorClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* effectively decodes a mnemonic phrase into a public punk address.
|
||||
* @param mnemonic A mnemonic from which to generate a public/private keypair.
|
||||
* @returns the address for this client wallet
|
||||
*/
|
||||
@@ -277,6 +316,12 @@ export default class ValidatorClient {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* async func to build/create a NYM wallet.
|
||||
* @param mnemonic
|
||||
* @param prefix
|
||||
* @returns Promise<DirectSecp256k1HdWallet>
|
||||
*/
|
||||
static async buildWallet(
|
||||
mnemonic: string,
|
||||
prefix: string
|
||||
@@ -285,12 +330,22 @@ export default class ValidatorClient {
|
||||
return DirectSecp256k1HdWallet.fromMnemonic(mnemonic, signerOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* this method returns a promise which returns the amount/denom of a given user
|
||||
* @param address the user's public contract address eg. `punk23o85698370891702470413487`
|
||||
* @returns user's instance of the Validator Client.
|
||||
*/
|
||||
getBalance(address: string): Promise<Coin | null> {
|
||||
return this.client
|
||||
.getBalance(address, this.denom)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Func calls the client (instance of INetClient / NetClient) method `getStateParams(addr)`
|
||||
* Used in minimumMixnodeBond() and minimumGatewayBond()
|
||||
* @returns Promisified State Params.
|
||||
*/
|
||||
async getStateParams(): Promise<StateParams> {
|
||||
return this.client
|
||||
.getStateParams(this.contractAddress)
|
||||
@@ -305,9 +360,9 @@ export default class ValidatorClient {
|
||||
* TODO: We will want to put this puppy on a timer, but for the moment we can
|
||||
* just get things strung together and refresh it manually.
|
||||
*/
|
||||
refreshMixNodes(): Promise<MixNodeBond[]> {
|
||||
async refreshMixNodes(arg: string): Promise<MixNodeBond[]> {
|
||||
return this.mixNodesCache
|
||||
.refreshMixNodes(this.contractAddress)
|
||||
.refreshMixNodes(arg)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
}
|
||||
|
||||
@@ -319,7 +374,7 @@ export default class ValidatorClient {
|
||||
* TODO: We will want to put this puppy on a timer, but for the moment we can
|
||||
* just get things strung together and refresh it manually.
|
||||
*/
|
||||
refreshValidatorAPIMixNodes(): Promise<MixNodeBond[]> {
|
||||
async refreshValidatorAPIMixNodes(): Promise<MixNodeBond[]> {
|
||||
return this.mixNodesCache
|
||||
.refreshValidatorAPIMixNodes(this.urls)
|
||||
.catch((err) => this.handleRequestFailure(err));
|
||||
@@ -397,6 +452,7 @@ export default class ValidatorClient {
|
||||
amount: Coin
|
||||
): Promise<ExecuteResult> {
|
||||
if (this.client instanceof NetClient) {
|
||||
console.log("args mixID ", mixIdentity, " amount ", amount);
|
||||
const result = await this.client
|
||||
.executeContract(
|
||||
this.client.clientAddress,
|
||||
@@ -747,6 +803,10 @@ export default class ValidatorClient {
|
||||
|
||||
/**
|
||||
* Send funds multiple times from one address to another in a single block.
|
||||
* @param senderAddress
|
||||
* @param data
|
||||
* @param memo
|
||||
* @returns result
|
||||
*/
|
||||
async sendMultiple(
|
||||
senderAddress: string,
|
||||
|
||||
Reference in New Issue
Block a user