Merge branch 'make-faucet-configurable' into 0.22

This commit is contained in:
Simon Warta 2020-08-11 09:54:06 +02:00
commit 332ad4e98e
16 changed files with 165 additions and 66 deletions

View File

@ -7,6 +7,9 @@
- @cosmjs/launchpad: Add ed25519 support to `encodeBech32Pubkey`.
- @cosmjs/launchpad: Add `encodeAminoPubkey` and `decodeAminoPubkey`.
- @cosmjs/utils: Add `arrayContentEquals`.
- @cosmjs/faucet: Add config variables `FAUCET_ADDRESS_PREFIX` and
`FAUCET_TOKENS`.
- @cosmjs/faucet: Remove broken chain ID from `cosmwasm-faucet generate`.
## 0.22.0 (2020-08-03)

View File

@ -38,7 +38,6 @@ help Shows a help text and exits
version Prints the version and exits
generate Generates a random mnemonic, shows derived faucet addresses and exits
1 Chain ID
start Starts the faucet
1 Node base URL, e.g. http://localhost:1317
@ -49,6 +48,10 @@ FAUCET_CONCURRENCY Number of distributor accounts. Defaults to 5.
FAUCET_PORT Port of the webserver. Defaults to 8000.
FAUCET_MNEMONIC Secret mnemonic that serves as the base secret for the
faucet HD accounts
FAUCET_ADDRESS_PREFIX The bech32 address prefix. Defaults to "cosmos".
FAUCET_TOKENS A comma separated list of tokens configs in the format
{DISPLAY}=10^{DIGITS}{base}, e.g.
"ATOM=10^6uatom" or "COSM = 10^6ucosm, STAKE = 10^3mstake".
FAUCET_CREDIT_AMOUNT_TKN Send this amount of TKN to a user requesting TKN. TKN is
a placeholder for the token ticker. Defaults to 10.
FAUCET_REFILL_FACTOR Send factor times credit amount on refilling. Defauls to 8.

View File

@ -22,7 +22,7 @@
"access": "public"
},
"scripts": {
"dev-start": "FAUCET_CREDIT_AMOUNT_COSM=10 FAUCET_CREDIT_AMOUNT_STAKE=5 FAUCET_CONCURRENCY=3 FAUCET_MNEMONIC=\"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone\" ./bin/cosmwasm-faucet start \"http://localhost:1317\"",
"dev-start": "yarn start-dev",
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
"format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"",
"lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"",
@ -31,7 +31,9 @@
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
"test-node": "node jasmine-testrunner.js",
"test": "yarn build-or-skip && yarn test-node",
"coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet"
"coverage": "nyc --reporter=text --reporter=lcov yarn test --quiet",
"start-dev": "FAUCET_CREDIT_AMOUNT_COSM=10 FAUCET_CREDIT_AMOUNT_STAKE=5 FAUCET_CONCURRENCY=3 FAUCET_MNEMONIC=\"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone\" ./bin/cosmwasm-faucet start \"http://localhost:1317\"",
"start-coralnet": "FAUCET_ADDRESS_PREFIX=coral FAUCET_TOKENS=\"SHELL=10^6ushell, REEF=10^6ureef\" FAUCET_CREDIT_AMOUNT_SHELL=10 FAUCET_CREDIT_AMOUNT_REEF=2 FAUCET_CONCURRENCY=3 FAUCET_MNEMONIC=\"economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone\" ./bin/cosmwasm-faucet start \"https://lcd.coralnet.cosmwasm.com\""
},
"dependencies": {
"@cosmjs/crypto": "^0.22.0",

View File

@ -4,17 +4,15 @@ import * as constants from "../constants";
import { createWallets } from "../profile";
export async function generate(args: readonly string[]): Promise<void> {
if (args.length < 1) {
throw Error(
`Not enough arguments for action 'generate'. See '${constants.binaryName} help' or README for arguments.`,
if (args.length > 0) {
console.warn(
`Warning: ${constants.binaryName} generate does not require positional arguments anymore. Use env variables FAUCET_ADDRESS_PREFIX or FAUCET_CONCURRENCY to configure how accounts are created.`,
);
}
const chainId = args[0];
const mnemonic = Bip39.encode(Random.getBytes(16)).toString();
console.info(`FAUCET_MNEMONIC="${mnemonic}"`);
// Log the addresses
await createWallets(mnemonic, chainId, constants.concurrency, true);
await createWallets(mnemonic, constants.addressPrefix, constants.concurrency, true);
}

View File

@ -11,7 +11,6 @@ help Shows a help text and exits
version Prints the version and exits
generate Generates a random mnemonic, shows derived faucet addresses and exits
1 Chain ID
start Starts the faucet
1 Node base URL, e.g. http://localhost:1317
@ -22,6 +21,10 @@ FAUCET_CONCURRENCY Number of distributor accounts. Defaults to 5.
FAUCET_PORT Port of the webserver. Defaults to 8000.
FAUCET_MNEMONIC Secret mnemonic that serves as the base secret for the
faucet HD accounts
FAUCET_ADDRESS_PREFIX The bech32 address prefix. Defaults to "cosmos".
FAUCET_TOKENS A comma separated list of tokens configs in the format
{DISPLAY}=10^{DIGITS}{base}, e.g.
"ATOM=10^6uatom" or "COSM = 10^6ucosm, STAKE = 10^3mstake".
FAUCET_CREDIT_AMOUNT_TKN Send this amount of TKN to a user requesting TKN. TKN is
a placeholder for the token ticker. Defaults to 10.
FAUCET_REFILL_FACTOR Send factor times credit amount on refilling. Defauls to 8.

View File

@ -1,9 +1,9 @@
import { CosmosClient } from "@cosmjs/launchpad";
import { Webserver } from "../../api/webserver";
import * as constants from "../../constants";
import { logAccountsState } from "../../debugging";
import { Faucet } from "../../faucet";
import { Webserver } from "../api/webserver";
import * as constants from "../constants";
import { logAccountsState } from "../debugging";
import { Faucet } from "../faucet";
export async function start(args: readonly string[]): Promise<void> {
if (args.length < 1) {
@ -23,7 +23,7 @@ export async function start(args: readonly string[]): Promise<void> {
const faucet = await Faucet.make(
blockchainBaseUrl,
constants.addressPrefix,
constants.developmentTokenConfig,
constants.tokenConfig,
constants.mnemonic,
constants.concurrency,
true,
@ -31,7 +31,7 @@ export async function start(args: readonly string[]): Promise<void> {
const chainTokens = faucet.loadTokenTickers();
console.info("Chain tokens:", chainTokens);
const accounts = await faucet.loadAccounts();
logAccountsState(accounts, constants.developmentTokenConfig);
logAccountsState(accounts, constants.tokenConfig);
let availableTokens = await faucet.availableTokens();
console.info("Available tokens:", availableTokens);
setInterval(async () => {
@ -45,4 +45,5 @@ export async function start(args: readonly string[]): Promise<void> {
console.info("Creating webserver ...");
const server = new Webserver(faucet, { nodeUrl: blockchainBaseUrl, chainId: chainId });
server.start(constants.port);
console.info(`Try "curl -sS http://localhost:${constants.port}/status | jq" to check the status.`);
}

View File

@ -1 +0,0 @@
export { start } from "./start";

View File

@ -1,24 +1,11 @@
import { TokenConfiguration } from "./types";
import { TokenConfiguration } from "./tokenmanager";
import { parseBankTokens } from "./tokens";
export const binaryName = "cosmwasm-faucet";
export const concurrency: number = Number.parseInt(process.env.FAUCET_CONCURRENCY || "", 10) || 5;
export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) || 8000;
export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC;
export const addressPrefix = "cosmos";
/** For the local development chain */
export const developmentTokenConfig: TokenConfiguration = {
bankTokens: [
{
fractionalDigits: 6,
tickerSymbol: "COSM",
denom: "ucosm",
},
{
fractionalDigits: 6,
tickerSymbol: "STAKE",
denom: "ustake",
},
],
export const addressPrefix = process.env.FAUCET_ADDRESS_PREFIX || "cosmos";
export const tokenConfig: TokenConfiguration = {
bankTokens: parseBankTokens(process.env.FAUCET_TOKENS || "COSM=10^6ucosm, STAKE=10^6ustake"),
};

View File

@ -1,7 +1,8 @@
import { Coin } from "@cosmjs/launchpad";
import { Decimal } from "@cosmjs/math";
import { MinimalAccount, SendJob, TokenConfiguration } from "./types";
import { TokenConfiguration } from "./tokenmanager";
import { MinimalAccount, SendJob } from "./types";
/** A string representation of a coin in a human-readable format that can change at any time */
function debugCoin(coin: Coin, tokens: TokenConfiguration): string {

View File

@ -4,7 +4,7 @@ import { CosmosClient } from "@cosmjs/launchpad";
import { assert } from "@cosmjs/utils";
import { Faucet } from "./faucet";
import { TokenConfiguration } from "./types";
import { TokenConfiguration } from "./tokenmanager";
function pendingWithoutWasmd(): void {
if (!process.env.WASMD_ENABLED) {

View File

@ -3,8 +3,8 @@ import { sleep } from "@cosmjs/utils";
import { debugAccount, logAccountsState, logSendJob } from "./debugging";
import { createWallets } from "./profile";
import { TokenManager } from "./tokenmanager";
import { MinimalAccount, SendJob, TokenConfiguration } from "./types";
import { TokenConfiguration, TokenManager } from "./tokenmanager";
import { MinimalAccount, SendJob } from "./types";
function isDefined<X>(value: X | undefined): value is X {
return value !== undefined;
@ -119,7 +119,7 @@ export class Faucet {
public async refill(): Promise<void> {
if (this.logging) {
console.info(`Connected to network: ${this.readOnlyClient.getChainId()}`);
console.info(`Connected to network: ${await this.readOnlyClient.getChainId()}`);
console.info(`Tokens on network: ${this.loadTokenTickers().join(", ")}`);
}

View File

@ -1,5 +1,5 @@
import { TokenManager } from "./tokenmanager";
import { MinimalAccount, TokenConfiguration } from "./types";
import { TokenConfiguration, TokenManager } from "./tokenmanager";
import { MinimalAccount } from "./types";
const dummyConfig: TokenConfiguration = {
bankTokens: [

View File

@ -1,7 +1,8 @@
import { Coin } from "@cosmjs/launchpad";
import { Decimal, Uint53 } from "@cosmjs/math";
import { BankTokenMeta, MinimalAccount, TokenConfiguration } from "./types";
import { BankTokenMeta } from "./tokens";
import { MinimalAccount } from "./types";
/** Send `factor` times credit amount on refilling */
const defaultRefillFactor = 20;
@ -9,6 +10,11 @@ const defaultRefillFactor = 20;
/** refill when balance gets below `factor` times credit amount */
const defaultRefillThresholdFactor = 8;
export interface TokenConfiguration {
/** Supported tokens of the Cosmos SDK bank module */
readonly bankTokens: readonly BankTokenMeta[];
}
export class TokenManager {
private readonly config: TokenConfiguration;

View File

@ -0,0 +1,78 @@
import { parseBankToken, parseBankTokens } from "./tokens";
describe("tokens", () => {
describe("parseBankToken", () => {
it("works", () => {
expect(parseBankToken("COSM=10^6ucosm")).toEqual({
tickerSymbol: "COSM",
fractionalDigits: 6,
denom: "ucosm",
});
});
it("allows using whitespace", () => {
expect(parseBankToken("COSM = 10^6 ucosm")).toEqual({
tickerSymbol: "COSM",
fractionalDigits: 6,
denom: "ucosm",
});
});
});
describe("parseBankTokens", () => {
it("works for one", () => {
expect(parseBankTokens("COSM=10^6ucosm")).toEqual([
{
tickerSymbol: "COSM",
fractionalDigits: 6,
denom: "ucosm",
},
]);
});
it("works for two", () => {
expect(parseBankTokens("COSM=10^6ucosm,STAKE=10^3mstake")).toEqual([
{
tickerSymbol: "COSM",
fractionalDigits: 6,
denom: "ucosm",
},
{
tickerSymbol: "STAKE",
fractionalDigits: 3,
denom: "mstake",
},
]);
});
it("ignores whitespace", () => {
expect(parseBankTokens("COSM=10^6ucosm, STAKE=10^3mstake\n")).toEqual([
{
tickerSymbol: "COSM",
fractionalDigits: 6,
denom: "ucosm",
},
{
tickerSymbol: "STAKE",
fractionalDigits: 3,
denom: "mstake",
},
]);
});
it("ignores empty elements", () => {
expect(parseBankTokens("COSM=10^6ucosm,STAKE=10^3mstake,")).toEqual([
{
tickerSymbol: "COSM",
fractionalDigits: 6,
denom: "ucosm",
},
{
tickerSymbol: "STAKE",
fractionalDigits: 3,
denom: "mstake",
},
]);
});
});
});

View File

@ -0,0 +1,41 @@
import { Uint53 } from "@cosmjs/math";
export interface BankTokenMeta {
readonly denom: string;
/**
* The token ticker symbol, e.g. ATOM or ETH.
*/
readonly tickerSymbol: string;
/**
* The number of fractional digits the token supports.
*
* A quantity is expressed as atomic units. 10^fractionalDigits of those
* atomic units make up 1 token.
*
* E.g. in Ethereum 10^18 wei are 1 ETH and from the quantity 123000000000000000000
* the last 18 digits are the fractional part and the rest the wole part.
*/
readonly fractionalDigits: number;
}
const parseBankTokenPattern = /^([a-zA-Z]{2,20})=10\^([0-9]+)([a-zA-Z]{2,20})$/;
export function parseBankToken(input: string): BankTokenMeta {
const match = input.replace(/\s/g, "").match(parseBankTokenPattern);
if (!match) {
throw new Error("Token could not be parsed. Format: {DISPLAY}=10^{DIGITS}{base}, e.g. ATOM=10^6uatom");
}
return {
tickerSymbol: match[1],
fractionalDigits: Uint53.fromString(match[2]).toNumber(),
denom: match[3],
};
}
export function parseBankTokens(input: string): BankTokenMeta[] {
return input
.trim()
.split(",")
.filter((part) => part.trim() !== "")
.map((part) => parseBankToken(part));
}

View File

@ -6,27 +6,4 @@ export interface SendJob {
readonly amount: Coin;
}
export interface BankTokenMeta {
readonly denom: string;
/**
* The token ticker symbol, e.g. ATOM or ETH.
*/
readonly tickerSymbol: string;
/**
* The number of fractional digits the token supports.
*
* A quantity is expressed as atomic units. 10^fractionalDigits of those
* atomic units make up 1 token.
*
* E.g. in Ethereum 10^18 wei are 1 ETH and from the quantity 123000000000000000000
* the last 18 digits are the fractional part and the rest the wole part.
*/
readonly fractionalDigits: number;
}
export interface TokenConfiguration {
/** Supported tokens of the Cosmos SDK bank module */
readonly bankTokens: readonly BankTokenMeta[];
}
export type MinimalAccount = Pick<Account, "address" | "balance">;