mirror of
https://github.com/luxfi/docs.git
synced 2026-09-24 00:25:32 +00:00
fix: switch to npm @hanzo/docs packages and fix API call
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
// @ts-nocheck
|
||||
import { browser } from '@hanzo/docs-mdx/runtime/browser';
|
||||
import type * as Config from '../source.config';
|
||||
|
||||
const create = browser<typeof Config, import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
|
||||
DocData: {
|
||||
}
|
||||
}>();
|
||||
const browserCollections = {
|
||||
docs: create.doc("docs", {"index.mdx": () => import("../content/docs/index.mdx?collection=docs"), "build/index.mdx": () => import("../content/docs/build/index.mdx?collection=docs"), "learn/architecture.mdx": () => import("../content/docs/learn/architecture.mdx?collection=docs"), "learn/index.mdx": () => import("../content/docs/learn/index.mdx?collection=docs"), "learn/tokenomics.mdx": () => import("../content/docs/learn/tokenomics.mdx?collection=docs"), "nodes/index.mdx": () => import("../content/docs/nodes/index.mdx?collection=docs"), "reference/index.mdx": () => import("../content/docs/reference/index.mdx?collection=docs"), }),
|
||||
};
|
||||
export default browserCollections;
|
||||
@@ -0,0 +1,8 @@
|
||||
// @ts-nocheck
|
||||
import { dynamic } from '@hanzo/docs-mdx/runtime/dynamic';
|
||||
import * as Config from '../source.config';
|
||||
|
||||
const create = await dynamic<typeof Config, import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
|
||||
DocData: {
|
||||
}
|
||||
}>(Config, {"configPath":"source.config.ts","environment":"next","outDir":".docs"}, {"doc":{"passthroughs":["extractedReferences"]}});
|
||||
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import { frontmatter as __fd_glob_11 } from "../content/docs/reference/index.mdx?collection=docs&only=frontmatter"
|
||||
import { frontmatter as __fd_glob_10 } from "../content/docs/nodes/index.mdx?collection=docs&only=frontmatter"
|
||||
import { frontmatter as __fd_glob_9 } from "../content/docs/learn/tokenomics.mdx?collection=docs&only=frontmatter"
|
||||
import { frontmatter as __fd_glob_8 } from "../content/docs/learn/index.mdx?collection=docs&only=frontmatter"
|
||||
import { frontmatter as __fd_glob_7 } from "../content/docs/learn/architecture.mdx?collection=docs&only=frontmatter"
|
||||
import { frontmatter as __fd_glob_6 } from "../content/docs/build/index.mdx?collection=docs&only=frontmatter"
|
||||
import { frontmatter as __fd_glob_5 } from "../content/docs/index.mdx?collection=docs&only=frontmatter"
|
||||
import { default as __fd_glob_4 } from "../content/docs/reference/meta.json?collection=docs"
|
||||
import { default as __fd_glob_3 } from "../content/docs/nodes/meta.json?collection=docs"
|
||||
import { default as __fd_glob_2 } from "../content/docs/learn/meta.json?collection=docs"
|
||||
import { default as __fd_glob_1 } from "../content/docs/build/meta.json?collection=docs"
|
||||
import { default as __fd_glob_0 } from "../content/docs/meta.json?collection=docs"
|
||||
import { server } from '@hanzo/docs-mdx/runtime/server';
|
||||
import type * as Config from '../source.config';
|
||||
|
||||
const create = server<typeof Config, import("@hanzo/docs-mdx/runtime/types").InternalTypeConfig & {
|
||||
DocData: {
|
||||
}
|
||||
}>({"doc":{"passthroughs":["extractedReferences"]}});
|
||||
|
||||
export const docs = await create.docsLazy("docs", "content/docs", {"meta.json": __fd_glob_0, "build/meta.json": __fd_glob_1, "learn/meta.json": __fd_glob_2, "nodes/meta.json": __fd_glob_3, "reference/meta.json": __fd_glob_4, }, {"index.mdx": __fd_glob_5, "build/index.mdx": __fd_glob_6, "learn/architecture.mdx": __fd_glob_7, "learn/index.mdx": __fd_glob_8, "learn/tokenomics.mdx": __fd_glob_9, "nodes/index.mdx": __fd_glob_10, "reference/index.mdx": __fd_glob_11, }, {"index.mdx": () => import("../content/docs/index.mdx?collection=docs"), "build/index.mdx": () => import("../content/docs/build/index.mdx?collection=docs"), "learn/architecture.mdx": () => import("../content/docs/learn/architecture.mdx?collection=docs"), "learn/index.mdx": () => import("../content/docs/learn/index.mdx?collection=docs"), "learn/tokenomics.mdx": () => import("../content/docs/learn/tokenomics.mdx?collection=docs"), "nodes/index.mdx": () => import("../content/docs/nodes/index.mdx?collection=docs"), "reference/index.mdx": () => import("../content/docs/reference/index.mdx?collection=docs"), });
|
||||
@@ -0,0 +1,28 @@
|
||||
// source.config.ts
|
||||
import { defineConfig, defineDocs } from "@hanzo/docs/mdx/config";
|
||||
var docs = defineDocs({
|
||||
docs: {
|
||||
async: true
|
||||
},
|
||||
meta: {}
|
||||
});
|
||||
var source_config_default = defineConfig({
|
||||
mdxOptions: async () => {
|
||||
const { rehypeCodeDefaultOptions } = await import("@hanzo/docs/core/mdx-plugins/rehype-code");
|
||||
return {
|
||||
rehypeCodeOptions: {
|
||||
langs: ["ts", "js", "html", "tsx", "mdx", "go", "rust", "bash", "json", "yaml", "toml"],
|
||||
inline: "tailing-curly-colon",
|
||||
themes: {
|
||||
light: "github-light",
|
||||
dark: "github-dark"
|
||||
},
|
||||
transformers: [...rehypeCodeDefaultOptions.transformers ?? []]
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export {
|
||||
source_config_default as default,
|
||||
docs
|
||||
};
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
# Genesis Flow: CLI -> Netrunner -> Node -> EVM
|
||||
|
||||
## Critical Investigation Summary
|
||||
|
||||
**Date**: 2025-12-27
|
||||
**Scope**: CLI (`/Users/z/work/lux/cli`) and Netrunner (`/Users/z/work/lux/netrunner`)
|
||||
**Finding**: `stateRoot` and `genesisHash` are NOT stored in genesis JSON - they are computed by EVM at runtime.
|
||||
|
||||
---
|
||||
|
||||
## 1. Genesis Structure Overview
|
||||
|
||||
The main genesis.json contains **three genesis configurations**:
|
||||
|
||||
```
|
||||
genesis.json (main)
|
||||
├── networkID: 1 (mainnet) | 2 (testnet) | ...
|
||||
├── allocations: [...] (P-Chain initial allocations)
|
||||
├── initialStakers: [...] (validators)
|
||||
├── cChainGenesis: "{...}" (C-Chain genesis as JSON STRING)
|
||||
└── startTime, message, etc.
|
||||
```
|
||||
|
||||
**Key Point**: `cChainGenesis` is embedded as an **escaped JSON string**, not a nested object.
|
||||
|
||||
---
|
||||
|
||||
## 2. Genesis Source Files
|
||||
|
||||
### 2.1 Canonical Genesis Package
|
||||
|
||||
**Location**: `/Users/z/work/lux/genesis/configs/`
|
||||
|
||||
```
|
||||
genesis/configs/
|
||||
├── configs.go # Main loader with GetCanonicalGenesisBytes()
|
||||
├── mainnet/
|
||||
│ ├── network.json # Network params (networkID, allocations, etc.)
|
||||
│ ├── pchain.json # P-Chain validators
|
||||
│ └── cchain.json # C-Chain genesis (raw JSON)
|
||||
├── testnet/
|
||||
├── devnet/
|
||||
└── custom/
|
||||
```
|
||||
|
||||
**`configs.go` key function**:
|
||||
```go
|
||||
func GetCanonicalGenesisBytes(networkID uint32) ([]byte, error) {
|
||||
// Returns pre-serialized bytes to avoid JSON re-serialization
|
||||
// which would cause "db contains invalid genesis hash" errors
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
return mainnetFS.ReadFile("mainnet/genesis.json")
|
||||
case constants.TestnetID:
|
||||
return testnetFS.ReadFile("testnet/genesis.json")
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Netrunner Default Genesis
|
||||
|
||||
**Location**: `/Users/z/work/lux/netrunner/network/default/genesis.json`
|
||||
|
||||
This is the fallback genesis used for local/devnet when no canonical genesis exists.
|
||||
|
||||
---
|
||||
|
||||
## 3. Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ USER COMMAND │
|
||||
│ lux network start --mainnet OR lux chain deploy <name> │
|
||||
└──────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ CLI (cmd/networkcmd/start.go) │
|
||||
│ │
|
||||
│ 1. Parse flags (--mainnet, --testnet, etc.) │
|
||||
│ 2. Build globalNodeConfig: │
|
||||
│ { │
|
||||
│ "network-id": 1, │
|
||||
│ "db-type": "badgerdb", │
|
||||
│ "api-admin-enabled": true, <-- ADMIN API ENABLED HERE │
|
||||
│ "track-chains": "..." │
|
||||
│ } │
|
||||
│ 3. Create gRPC client to netrunner │
|
||||
│ 4. Call client.Start() with options │
|
||||
└──────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│ gRPC
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ NETRUNNER SERVER (server/network.go) │
|
||||
│ │
|
||||
│ createConfig() function: │
|
||||
│ │
|
||||
│ switch networkID { │
|
||||
│ case MainnetID: │
|
||||
│ cfg = local.NewCanonicalMainnetConfig(execPath, numNodes) │
|
||||
│ // Uses genesis/configs.GetCanonicalGenesisBytes(1) │
|
||||
│ case TestnetID: │
|
||||
│ cfg = local.NewCanonicalTestnetConfig(execPath, numNodes) │
|
||||
│ // Uses genesis/configs.GetCanonicalGenesisBytes(2) │
|
||||
│ default: │
|
||||
│ cfg = local.NewDefaultConfig(execPath) │
|
||||
│ // Uses embedded network/default/genesis.json │
|
||||
│ } │
|
||||
└──────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ NETRUNNER LOCAL (local/genesis_config.go) │
|
||||
│ │
|
||||
│ newCanonicalConfig() function: │
|
||||
│ │
|
||||
│ 1. genesisBytes := configs.GetCanonicalGenesisBytes(networkID) │
|
||||
│ // Returns RAW BYTES - no re-serialization! │
|
||||
│ │
|
||||
│ 2. Load validator keys from genesis/configs/keys/ │
|
||||
│ │
|
||||
│ 3. Build network.Config{ │
|
||||
│ Genesis: string(genesisBytes), <-- Genesis as string │
|
||||
│ NodeConfigs: [...], <-- Per-node configs │
|
||||
│ } │
|
||||
└──────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ NETRUNNER LOCAL (local/network.go) │
|
||||
│ │
|
||||
│ NewNetwork() function: │
|
||||
│ │
|
||||
│ 1. Create node directories: │
|
||||
│ ~/.lux/runs/{network}/run_{timestamp}/node{i}/ │
|
||||
│ │
|
||||
│ 2. Write genesis.json to each node: │
|
||||
│ node{i}/genesis.json <-- Same genesis for all nodes │
|
||||
│ │
|
||||
│ 3. Write node config: │
|
||||
│ node{i}/config.json │
|
||||
│ │
|
||||
│ 4. Write chain configs: │
|
||||
│ node{i}/chainConfigs/C/config.json │
|
||||
│ │
|
||||
│ 5. Start luxd processes with: │
|
||||
│ --genesis-file={node}/genesis.json │
|
||||
│ --config-file={node}/config.json │
|
||||
│ --chain-config-dir={node}/chainConfigs │
|
||||
└──────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ LUXD NODE │
|
||||
│ │
|
||||
│ 1. Load genesis.json from --genesis-file │
|
||||
│ │
|
||||
│ 2. Parse main genesis: │
|
||||
│ - Extract networkID, allocations, initialStakers │
|
||||
│ - Extract cChainGenesis as JSON string │
|
||||
│ │
|
||||
│ 3. Initialize chains: │
|
||||
│ - P-Chain: Uses allocations, initialStakers │
|
||||
│ - X-Chain: Uses xChainGenesis (if present) │
|
||||
│ - C-Chain: Passes cChainGenesis string to EVM plugin │
|
||||
└──────────────────────────────────┬──────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ EVM PLUGIN (coreth/evm) │
|
||||
│ │
|
||||
│ 1. Receive cChainGenesis JSON string from luxd │
|
||||
│ │
|
||||
│ 2. Parse into Genesis struct (core/genesis.go): │
|
||||
│ type Genesis struct { │
|
||||
│ Config *params.ChainConfig │
|
||||
│ Nonce uint64 │
|
||||
│ Timestamp uint64 │
|
||||
│ ExtraData []byte │
|
||||
│ GasLimit uint64 │
|
||||
│ Difficulty *big.Int │
|
||||
│ Alloc GenesisAlloc │
|
||||
│ // NOTE: NO stateRoot or genesisHash fields! │
|
||||
│ } │
|
||||
│ │
|
||||
│ 3. COMPUTE stateRoot from Alloc: │
|
||||
│ - Create state trie │
|
||||
│ - Apply all allocations (balances, code, storage) │
|
||||
│ - Compute Merkle root = stateRoot │
|
||||
│ │
|
||||
│ 4. COMPUTE genesisHash: │
|
||||
│ - Build genesis block header with computed stateRoot │
|
||||
│ - Hash header = genesisHash │
|
||||
│ │
|
||||
│ 5. Write genesis block to database │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Where stateRoot/genesisHash Are Lost (They Aren't!)
|
||||
|
||||
### 4.1 The Misconception
|
||||
|
||||
`stateRoot` and `genesisHash` are **NOT stored** in genesis.json because they are **computed values**, not input values.
|
||||
|
||||
### 4.2 How They Work
|
||||
|
||||
```
|
||||
INPUT COMPUTED
|
||||
┌──────────────────┐ ┌──────────────────────┐
|
||||
│ genesis.json │ │ Genesis Block │
|
||||
│ │ │ │
|
||||
│ - config │ │ - stateRoot │
|
||||
│ - alloc │──────────▶ │ - genesisHash │
|
||||
│ - timestamp │ COMPUTE │ - header fields │
|
||||
│ - gasLimit │ │ │
|
||||
│ - extraData │ │ │
|
||||
└──────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
### 4.3 When genesisHash Mismatch Occurs
|
||||
|
||||
**Problem**: If you:
|
||||
1. Re-serialize genesis.json (field order changes)
|
||||
2. Modify any alloc entry (balances, code, storage)
|
||||
3. Change any header field (timestamp, gasLimit, extraData)
|
||||
|
||||
**Result**: Different stateRoot → Different genesisHash → "db contains invalid genesis hash" error
|
||||
|
||||
### 4.4 Solution: Genesis Hash Override (EVM Feature)
|
||||
|
||||
The EVM plugin at `/Users/z/work/lux/evm/core/genesis.go` supports override fields:
|
||||
|
||||
```go
|
||||
type Genesis struct {
|
||||
// ... standard fields ...
|
||||
|
||||
// Override fields for block import compatibility
|
||||
GenesisHash *common.Hash `json:"genesisHash,omitempty"`
|
||||
StateRoot *common.Hash `json:"stateRoot,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
When these are set:
|
||||
- `toBlock()` uses `StateRoot` override (bypasses state computation)
|
||||
- `Commit()` uses `GenesisHash` override (bypasses hash computation)
|
||||
|
||||
---
|
||||
|
||||
## 5. Admin API Configuration
|
||||
|
||||
### 5.1 How Admin API Is Enabled
|
||||
|
||||
**CLI sets in globalNodeConfig**:
|
||||
```go
|
||||
// cmd/networkcmd/start.go
|
||||
globalNodeConfig := fmt.Sprintf(`{
|
||||
"network-id": %d,
|
||||
"db-type": "badgerdb",
|
||||
"api-admin-enabled": true, // <-- HERE
|
||||
"track-chains": %q
|
||||
}`, cfg.networkID, trackChainsValue)
|
||||
```
|
||||
|
||||
### 5.2 How It Flows to Nodes
|
||||
|
||||
1. CLI creates `globalNodeConfig` JSON string
|
||||
2. gRPC sends to netrunner via `client.WithGlobalNodeConfig()`
|
||||
3. Netrunner writes to `node{i}/config.json`
|
||||
4. luxd reads config and enables admin API
|
||||
|
||||
### 5.3 Verification
|
||||
|
||||
```bash
|
||||
# Check if admin API is enabled
|
||||
curl -X POST http://127.0.0.1:9650/ext/admin \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","method":"admin.getChainAliases","params":{"chain":"C"},"id":1}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. admin_importChain Support
|
||||
|
||||
### 6.1 CLI Implementation
|
||||
|
||||
**Location**: `/Users/z/work/lux/cli/cmd/chaincmd/import.go`
|
||||
|
||||
```go
|
||||
func callAdminImportChain(rpcEndpoint, filePath string) (bool, error) {
|
||||
req := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "admin_importChain",
|
||||
"params": []string{filePath},
|
||||
"id": 1,
|
||||
}
|
||||
// POST to RPC endpoint
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Usage
|
||||
|
||||
```bash
|
||||
# Import blocks to C-Chain
|
||||
lux chain import --rlp /path/to/blocks.rlp --rpc http://127.0.0.1:9650/ext/bc/C/rpc
|
||||
```
|
||||
|
||||
### 6.3 Known Issue: Import Persistence
|
||||
|
||||
**Problem**: Blocks imported via `admin_importChain` do not persist across network restarts.
|
||||
|
||||
**Root Cause**:
|
||||
- Blocks are written to BadgerDB
|
||||
- But "last accepted block" pointer is NOT updated in consensus layer
|
||||
- On restart, chain initializes from genesis
|
||||
|
||||
**Status**: Investigating fix in coreth/subnet-evm.
|
||||
|
||||
---
|
||||
|
||||
## 7. Chain Deploy Flow
|
||||
|
||||
### 7.1 Command
|
||||
|
||||
```bash
|
||||
lux chain deploy <chainName> --mainnet|--testnet
|
||||
```
|
||||
|
||||
### 7.2 Flow
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ CLI (cmd/chaincmd/deploy.go) │
|
||||
│ │
|
||||
│ 1. Load genesis from ~/.lux/chains/{name}/genesis.json │
|
||||
│ 2. Load sidecar from ~/.lux/chains/{name}/sidecar.json │
|
||||
│ 3. Validate EVM genesis format │
|
||||
│ 4. Call DeployToLocalNetwork() via gRPC │
|
||||
└────────────────────────────────┬───────────────────────────────┘
|
||||
│ gRPC
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ NETRUNNER │
|
||||
│ │
|
||||
│ 1. Receive subnet creation request │
|
||||
│ 2. Create subnet on P-Chain │
|
||||
│ 3. Create blockchain with provided genesis │
|
||||
│ 4. Track blockchain on all validators │
|
||||
│ 5. Write chain config to nodes │
|
||||
└────────────────────────────────┬───────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────────────────────────────────────────────┐
|
||||
│ LUXD NODES │
|
||||
│ │
|
||||
│ 1. Receive blockchain creation from P-Chain │
|
||||
│ 2. Load EVM plugin for chain │
|
||||
│ 3. Initialize chain with provided genesis │
|
||||
│ 4. Start processing blocks │
|
||||
└────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. File Reference
|
||||
|
||||
| Component | File | Purpose |
|
||||
|-----------|------|---------|
|
||||
| CLI | `cmd/networkcmd/start.go` | Network start command |
|
||||
| CLI | `cmd/chaincmd/deploy.go` | Chain deploy command |
|
||||
| CLI | `cmd/chaincmd/import.go` | Block import command |
|
||||
| Netrunner | `server/network.go` | gRPC server, config creation |
|
||||
| Netrunner | `local/network.go` | Local network management |
|
||||
| Netrunner | `local/genesis_config.go` | Genesis config factories |
|
||||
| Netrunner | `network/genesis.go` | Genesis loading/parsing |
|
||||
| Netrunner | `network/default/genesis.json` | Default genesis template |
|
||||
| Genesis | `configs/configs.go` | Canonical genesis loader |
|
||||
| Genesis | `configs/mainnet/*.json` | Mainnet genesis files |
|
||||
| EVM | `core/genesis.go` | Genesis struct, state computation |
|
||||
|
||||
---
|
||||
|
||||
## 9. Summary
|
||||
|
||||
1. **Genesis is stored as JSON** with cChainGenesis embedded as a string
|
||||
2. **stateRoot/genesisHash are NOT in genesis.json** - they are computed by EVM
|
||||
3. **Admin API is enabled** via `api-admin-enabled: true` in node config
|
||||
4. **admin_importChain exists** in CLI at `cmd/chaincmd/import.go`
|
||||
5. **Genesis hash mismatch** occurs when genesis is re-serialized or modified
|
||||
6. **Solution**: Use genesis hash override fields in EVM genesis
|
||||
|
||||
---
|
||||
|
||||
*Document generated from investigation of CLI and Netrunner codebases.*
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Official documentation for Lux Network - Post-quantum secure blockchain infrastructure.
|
||||
|
||||
Built with [Fumadocs](https://fumadocs.dev) and the Hanzo Docs framework.
|
||||
Built with [@hanzo/docs](https://hanzo.ai/docs) - the documentation framework by Hanzo AI.
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ import { source } from '@/lib/source';
|
||||
import {
|
||||
DocsPage,
|
||||
DocsBody,
|
||||
} from 'fumadocs-ui/page';
|
||||
} from '@hanzo/docs/ui/page';
|
||||
import { notFound } from 'next/navigation';
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { TypeTable } from 'fumadocs-ui/components/type-table';
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import defaultMdxComponents from '@hanzo/docs/ui/mdx';
|
||||
import { Tab, Tabs } from '@hanzo/docs/ui/components/tabs';
|
||||
import { Callout } from '@hanzo/docs/ui/components/callout';
|
||||
import { TypeTable } from '@hanzo/docs/ui/components/type-table';
|
||||
import { Accordion, Accordions } from '@hanzo/docs/ui/components/accordion';
|
||||
import { Step, Steps } from '@hanzo/docs/ui/components/steps';
|
||||
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ slug?: string[] }>;
|
||||
@@ -17,7 +18,9 @@ export default async function Page(props: {
|
||||
const page = source.getPage(params.slug);
|
||||
if (!page) notFound();
|
||||
|
||||
const { body: Mdx, toc } = await page.data.load();
|
||||
// Type assertion to work around TypeScript inference issues with MDX async loading
|
||||
const loadFn = (page.data as any).load;
|
||||
const { body: Mdx, toc } = await loadFn();
|
||||
|
||||
return (
|
||||
<DocsPage toc={toc}>
|
||||
@@ -35,6 +38,8 @@ export default async function Page(props: {
|
||||
TypeTable,
|
||||
Accordion,
|
||||
Accordions,
|
||||
Step,
|
||||
Steps,
|
||||
}}
|
||||
/>
|
||||
</DocsBody>
|
||||
|
||||
+8
-16
@@ -1,16 +1,21 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import { DocsLayout } from '@hanzo/docs/ui/layouts/docs';
|
||||
import { baseOptions, logo } from '@/lib/layout.shared';
|
||||
import { source } from '@/lib/source';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
const base = baseOptions();
|
||||
|
||||
return (
|
||||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
{...base}
|
||||
nav={{
|
||||
...base.nav,
|
||||
title: (
|
||||
<>
|
||||
<LuxIcon className="size-5" />
|
||||
<span className="font-medium">Lux Docs</span>
|
||||
{logo}
|
||||
<span className="font-semibold max-md:hidden">Lux Docs</span>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
@@ -22,16 +27,3 @@ export default function Layout({ children }: { children: ReactNode }) {
|
||||
</DocsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
function LuxIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'fumadocs-ui/css/neutral.css';
|
||||
@import 'fumadocs-ui/css/preset.css';
|
||||
@import '@hanzo/docs/ui/css/neutral.css';
|
||||
@import '@hanzo/docs/ui/css/preset.css';
|
||||
@import '@luxfi/brand/styles/docs.css';
|
||||
|
||||
@theme inline {
|
||||
|
||||
+6
-6
@@ -2,17 +2,17 @@ import './global.css';
|
||||
import type { Viewport, Metadata } from 'next';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import { RootProvider } from 'fumadocs-ui/provider/next';
|
||||
import { RootProvider } from '@hanzo/docs/ui/provider/next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
template: '%s | Lux Docs',
|
||||
default: 'Lux Docs',
|
||||
},
|
||||
description: 'Documentation for Lux Network - Post-quantum secure blockchain infrastructure.',
|
||||
description: 'Documentation for Lux Network - Post-quantum secure, multi-consensus blockchain infrastructure.',
|
||||
openGraph: {
|
||||
title: 'Lux Docs',
|
||||
description: 'Documentation for Lux Network - Post-quantum secure blockchain infrastructure.',
|
||||
description: 'Documentation for Lux Network - Post-quantum secure, multi-consensus blockchain infrastructure.',
|
||||
url: 'https://docs.lux.network',
|
||||
siteName: 'Lux Docs',
|
||||
},
|
||||
@@ -34,8 +34,8 @@ const mono = Geist_Mono({
|
||||
|
||||
export const viewport: Viewport = {
|
||||
themeColor: [
|
||||
{ media: '(prefers-color-scheme: dark)', color: '#0A0A0A' },
|
||||
{ media: '(prefers-color-scheme: light)', color: '#fff' },
|
||||
{ media: '(prefers-color-scheme: dark)', color: '#000000' },
|
||||
{ media: '(prefers-color-scheme: light)', color: '#ffffff' },
|
||||
],
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
className={`${geist.variable} ${mono.variable} brand-lux`}
|
||||
className={`${geist.variable} ${mono.variable}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<body className="flex flex-col min-h-screen">
|
||||
|
||||
+75
-37
@@ -1,57 +1,95 @@
|
||||
import Link from 'next/link';
|
||||
import { projectCategories } from '@/lib/layout.shared';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<main className="flex flex-1 flex-col items-center justify-center text-center px-4">
|
||||
<div className="max-w-3xl space-y-8">
|
||||
<div className="space-y-4">
|
||||
<main className="flex flex-1 flex-col px-4 py-16">
|
||||
<div className="max-w-6xl mx-auto w-full space-y-16">
|
||||
{/* Hero */}
|
||||
<div className="text-center space-y-6">
|
||||
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
|
||||
Lux Network
|
||||
</h1>
|
||||
<p className="text-xl text-fd-muted-foreground">
|
||||
<p className="text-xl text-fd-muted-foreground max-w-2xl mx-auto">
|
||||
Post-quantum secure, multi-consensus blockchain infrastructure
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-lg text-fd-muted-foreground max-w-2xl mx-auto">
|
||||
Build scalable, secure applications on Lux Network with our comprehensive
|
||||
documentation, SDKs, and developer tools.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="inline-flex items-center justify-center rounded-md bg-fd-primary text-fd-primary-foreground px-6 py-3 text-sm font-medium shadow transition-colors hover:bg-fd-primary/90"
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/luxfi"
|
||||
className="inline-flex items-center justify-center rounded-md border border-fd-border bg-fd-background px-6 py-3 text-sm font-medium shadow-sm transition-colors hover:bg-fd-accent hover:text-fd-accent-foreground"
|
||||
>
|
||||
View on GitHub
|
||||
</Link>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="inline-flex items-center justify-center rounded-md bg-fd-primary text-fd-primary-foreground px-6 py-3 text-sm font-medium shadow transition-colors hover:bg-fd-primary/90"
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/luxfi"
|
||||
className="inline-flex items-center justify-center rounded-md border border-fd-border bg-fd-background px-6 py-3 text-sm font-medium shadow-sm transition-colors hover:bg-fd-accent"
|
||||
>
|
||||
View on GitHub
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 pt-8">
|
||||
<div className="p-6 rounded-lg border border-fd-border bg-fd-card">
|
||||
<h3 className="font-semibold mb-2">Learn</h3>
|
||||
{/* Project Categories */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{projectCategories.map((category) => (
|
||||
<div
|
||||
key={category.title}
|
||||
className="p-6 rounded-xl border border-fd-border bg-fd-card"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<category.icon className="size-6 text-fd-primary" />
|
||||
<h2 className="text-xl font-semibold">{category.title}</h2>
|
||||
</div>
|
||||
<p className="text-sm text-fd-muted-foreground mb-4">
|
||||
{category.description}
|
||||
</p>
|
||||
<div className="space-y-2">
|
||||
{category.projects.map((project) => (
|
||||
<Link
|
||||
key={project.name}
|
||||
href={project.href}
|
||||
className="block p-3 rounded-lg hover:bg-fd-accent transition-colors"
|
||||
>
|
||||
<div className="font-medium">{project.name}</div>
|
||||
<div className="text-sm text-fd-muted-foreground">
|
||||
{project.description}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Quick Links */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<Link
|
||||
href="https://lux.network"
|
||||
className="p-6 rounded-lg border border-fd-border bg-fd-card hover:bg-fd-accent transition-colors"
|
||||
>
|
||||
<h3 className="font-semibold mb-2">Lux Network</h3>
|
||||
<p className="text-sm text-fd-muted-foreground">
|
||||
Understand Lux Network architecture, consensus, and post-quantum security.
|
||||
Main website and network information
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 rounded-lg border border-fd-border bg-fd-card">
|
||||
<h3 className="font-semibold mb-2">Build</h3>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://explore.lux.network"
|
||||
className="p-6 rounded-lg border border-fd-border bg-fd-card hover:bg-fd-accent transition-colors"
|
||||
>
|
||||
<h3 className="font-semibold mb-2">Explorer</h3>
|
||||
<p className="text-sm text-fd-muted-foreground">
|
||||
Deploy smart contracts and build dApps with our SDKs and tools.
|
||||
Blockchain explorer and analytics
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 rounded-lg border border-fd-border bg-fd-card">
|
||||
<h3 className="font-semibold mb-2">Run</h3>
|
||||
</Link>
|
||||
<Link
|
||||
href="https://wallet.lux.network"
|
||||
className="p-6 rounded-lg border border-fd-border bg-fd-card hover:bg-fd-accent transition-colors"
|
||||
>
|
||||
<h3 className="font-semibold mb-2">Wallet</h3>
|
||||
<p className="text-sm text-fd-muted-foreground">
|
||||
Operate validators, run nodes, and participate in the network.
|
||||
Manage your LUX tokens and assets
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: LUX Tokenomics
|
||||
description: LUX token economics, denominations, and supply mechanics
|
||||
---
|
||||
|
||||
# LUX Token Economics
|
||||
|
||||
LUX is the native token of the Lux Network, used for staking, transaction fees, and governance.
|
||||
|
||||
## Token Denominations
|
||||
|
||||
LUX uses **6 decimal places** on the P-Chain and X-Chain, similar to USDC/USDT. This allows for a maximum supply of approximately 18.4 trillion LUX within uint64 constraints.
|
||||
|
||||
| Unit | Symbol | Value in LUX | Value in µLUX |
|
||||
|------|--------|--------------|---------------|
|
||||
| MicroLux | µLUX | 0.000001 | 1 |
|
||||
| MilliLux | mLUX | 0.001 | 1,000 |
|
||||
| **Lux** | **LUX** | **1** | **1,000,000** |
|
||||
| KiloLux | kLUX | 1,000 | 10^9 |
|
||||
| MegaLux | MLUX | 1,000,000 | 10^12 |
|
||||
| GigaLux | GLUX | 1,000,000,000 | 10^15 |
|
||||
| TeraLux | TLUX | 1,000,000,000,000 | 10^18 |
|
||||
|
||||
### Minimum Transferable Unit
|
||||
|
||||
The smallest transferable unit on the P-Chain is **1 µLUX (microLUX) = 0.000001 LUX**.
|
||||
|
||||
### Why 6 Decimals?
|
||||
|
||||
- **Maximum Supply**: With 6 decimals, uint64 can represent up to ~18.4 trillion LUX
|
||||
- **Target Supply Cap**: 2 trillion LUX maximum supply
|
||||
- **Practical Minimums**: 0.000001 LUX is small enough for all practical use cases
|
||||
- **Compatibility**: Matches USDC/USDT decimal precision
|
||||
|
||||
## Chain-Specific Denominations
|
||||
|
||||
### P-Chain / X-Chain
|
||||
- Uses **6 decimals** (µLUX base unit)
|
||||
- Native LUX token for staking and transfers
|
||||
- Validator rewards paid in LUX
|
||||
|
||||
### C-Chain (EVM)
|
||||
- Uses **18 decimals** (Wei base unit, standard EVM)
|
||||
- 1 LUX = 10^18 Wei on C-Chain
|
||||
- Gas fees denominated in Wei
|
||||
|
||||
## Supply Mechanics
|
||||
|
||||
### Maximum Supply
|
||||
- **Supply Cap**: 2,000,000,000,000 LUX (2 trillion)
|
||||
- Enforced by the reward calculator
|
||||
|
||||
### Initial Distribution
|
||||
- Genesis allocations with unlock schedules
|
||||
- Validator staking rewards
|
||||
- No pre-mine beyond documented allocations
|
||||
|
||||
### Reward Calculation
|
||||
|
||||
Validators earn rewards based on:
|
||||
- Stake duration (longer = higher rate)
|
||||
- Stake amount relative to total supply
|
||||
- Remaining supply (rewards decrease as supply approaches cap)
|
||||
|
||||
```
|
||||
MintingRate = MinRate + (MaxRate - MinRate) × (StakeDuration / MaxDuration)
|
||||
Reward = RemainingSupply × (StakeAmount / TotalSupply) × MintingRate × (StakeDuration / MaxDuration)
|
||||
```
|
||||
|
||||
### Reward Config Parameters
|
||||
|
||||
| Parameter | Mainnet Value |
|
||||
|-----------|---------------|
|
||||
| MinConsumptionRate | 10% annually |
|
||||
| MaxConsumptionRate | 12% annually |
|
||||
| MintingPeriod | 365 days |
|
||||
| SupplyCap | 2 TLUX |
|
||||
|
||||
## Staking Requirements
|
||||
|
||||
### Validators
|
||||
- **Minimum Stake**: 2,000 LUX
|
||||
- **Maximum Stake**: 3,000,000 LUX
|
||||
- **Minimum Duration**: 2 weeks
|
||||
- **Maximum Duration**: 1 year
|
||||
- **Uptime Requirement**: 80%
|
||||
|
||||
### Delegators
|
||||
- **Minimum Stake**: 25 LUX
|
||||
- **Delegation Fee**: Minimum 2%
|
||||
|
||||
## Transaction Fees
|
||||
|
||||
| Operation | Fee |
|
||||
|-----------|-----|
|
||||
| Standard Transaction | 0.001 LUX (1 mLUX) |
|
||||
| Create Asset | 0.01 LUX (10 mLUX) |
|
||||
|
||||
## Code References
|
||||
|
||||
The denomination constants are defined in:
|
||||
|
||||
- **Node**: `node/utils/units/lux.go`
|
||||
- **Genesis**: `genesis/pkg/genesis/params.go`
|
||||
|
||||
```go
|
||||
// LUX uses 6 decimals (1 LUX = 10^6 microLUX)
|
||||
const (
|
||||
MicroLux uint64 = 1 // 0.000001 LUX
|
||||
MilliLux uint64 = 1000 * MicroLux // 0.001 LUX
|
||||
Lux uint64 = 1000 * MilliLux // 1 LUX
|
||||
KiloLux uint64 = 1000 * Lux // 1,000 LUX
|
||||
MegaLux uint64 = 1000 * KiloLux // 1,000,000 LUX
|
||||
GigaLux uint64 = 1000 * MegaLux // 1 billion LUX
|
||||
TeraLux uint64 = 1000 * GigaLux // 1 trillion LUX
|
||||
)
|
||||
```
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
|
||||
import type { BaseLayoutProps } from '@hanzo/docs/ui/layouts/shared';
|
||||
import { BookOpenIcon, CodeIcon, ServerIcon, WalletIcon, ShieldIcon, NetworkIcon } from 'lucide-react';
|
||||
|
||||
// Lux Network Icon
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { docs } from '@/.source';
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
import { docs } from '@/.docs/server';
|
||||
import { loader } from '@hanzo/docs/core/source';
|
||||
|
||||
export const source = loader({
|
||||
baseUrl: '/docs',
|
||||
|
||||
+4
-1
@@ -1,9 +1,12 @@
|
||||
import defaultComponents from 'fumadocs-ui/mdx';
|
||||
import defaultComponents from '@hanzo/docs/ui/mdx';
|
||||
import { Steps, Step } from '@hanzo/docs/ui/components/steps';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function useMDXComponents(components: Record<string, any>): Record<string, any> {
|
||||
return {
|
||||
...defaultComponents,
|
||||
Steps,
|
||||
Step,
|
||||
...components,
|
||||
};
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
import { createMDX } from 'fumadocs-mdx/next';
|
||||
import { createMDX } from '@hanzo/docs/mdx/next';
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const config: NextConfig = {
|
||||
@@ -14,6 +14,8 @@ const config: NextConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
const withMDX = createMDX();
|
||||
const withMDX = createMDX({
|
||||
outDir: '.docs',
|
||||
});
|
||||
|
||||
export default withMDX(config);
|
||||
|
||||
+27
-25
@@ -5,35 +5,37 @@
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "next dev",
|
||||
"lint": "fumadocs-mdx && next lint",
|
||||
"lint": "hanzo-docs-mdx && next lint",
|
||||
"start": "next start",
|
||||
"types:check": "fumadocs-mdx && tsc --noEmit"
|
||||
"types:check": "hanzo-docs-mdx && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@luxfi/brand": "file:../brand",
|
||||
"@fumadocs/mdx-remote": "^1.2.0",
|
||||
"fumadocs-core": "16.2.5",
|
||||
"fumadocs-mdx": "14.1.0",
|
||||
"fumadocs-ui": "16.2.5",
|
||||
"lucide-react": "^0.469.0",
|
||||
"next": "^15.1.0",
|
||||
"next-themes": "^0.4.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"shiki": "^3.0.0",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"zod": "^3.24.0"
|
||||
"@hanzo/docs": "^16.2.9",
|
||||
"@hanzo/docs-core": "^16.4.3",
|
||||
"@hanzo/docs-base-ui": "^16.4.3",
|
||||
"@hanzo/docs-ui": "^16.4.3",
|
||||
"@hanzo/docs-mdx": "^14.2.5",
|
||||
"@hanzo/docs-openapi": "^10.2.3",
|
||||
"@hanzo/docs-typescript": "^5.0.1",
|
||||
"@hanzo/docs-twoslash": "^3.1.11",
|
||||
"@hanzo/docs-docgen": "^3.0.5",
|
||||
"lucide-react": "^0.561.0",
|
||||
"next": "^16.0.10",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"shiki": "^3.20.0",
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"zod": "^4.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.0.0",
|
||||
"@types/node": "^22.10.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends browserslist-config-baseline/2024"
|
||||
]
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@types/node": "^24.10.2",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2752
-487
File diff suppressed because it is too large
Load Diff
+3
-18
@@ -1,31 +1,16 @@
|
||||
import {
|
||||
defineConfig,
|
||||
defineDocs,
|
||||
frontmatterSchema,
|
||||
metaSchema,
|
||||
} from 'fumadocs-mdx/config';
|
||||
import { z } from 'zod';
|
||||
import { defineConfig, defineDocs } from '@hanzo/docs/mdx/config';
|
||||
|
||||
export const docs = defineDocs({
|
||||
docs: {
|
||||
schema: frontmatterSchema.extend({
|
||||
preview: z.string().optional(),
|
||||
index: z.boolean().default(false),
|
||||
method: z.string().optional(),
|
||||
}),
|
||||
async: true,
|
||||
},
|
||||
meta: {
|
||||
schema: metaSchema.extend({
|
||||
description: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
meta: {},
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
mdxOptions: async () => {
|
||||
const { rehypeCodeDefaultOptions } = await import(
|
||||
'fumadocs-core/mdx-plugins/rehype-code'
|
||||
'@hanzo/docs/core/mdx-plugins/rehype-code'
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".source/**/*.ts",
|
||||
".docs/**/*.ts",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user