Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 61aec5a

Browse files
authored
Add difficuty util (#15)
* add difficulty util * add nonce animate
1 parent ab9086d commit 61aec5a

File tree

5 files changed

+105
-39
lines changed

5 files changed

+105
-39
lines changed

frontend/src/components/elevator/block-modal.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ import { useEffect, useState } from "preact/hooks";
33
import { ChainTheme, chainThemeAtom } from "../../states/atoms";
44
import { useAtomValue } from "jotai";
55
import { createBlockLink, createTransactionLink } from "../../util/link";
6-
import {
7-
BlockResponse,
8-
Network,
9-
TransactionStatus,
10-
TransactionType,
11-
TransactionTypeEnum,
12-
} from "../../service/type";
6+
import { BlockResponse, Network } from "../../service/type";
137
import { useChainService } from "../../context/chain";
148
import { ccc, Hex } from "@ckb-ccc/core";
159
import {
@@ -18,6 +12,7 @@ import {
1812
shannonToCKB,
1913
toShortHex,
2014
} from "../../util/type";
15+
import { compactToDifficulty, difficultyToEH } from "../../util/difficulty";
2116

2217
interface BlockModalProps {
2318
blockHash: Hex;
@@ -50,7 +45,9 @@ const BlockModal: preact.FunctionComponent<BlockModalProps> = ({
5045
const size = calcTotalTxSize(block?.transactions || []);
5146
const proposalTransactions = block?.proposalTransactions.length;
5247
const minerRewardCKB = shannonToCKB(+block?.miner.award);
53-
const difficulty = block?.blockHeader.compact_target;
48+
const difficulty = block?.blockHeader.compact_target
49+
? difficultyToEH(compactToDifficulty(block?.blockHeader.compact_target))
50+
: undefined;
5451
const nonce = block?.blockHeader.nonce
5552
? toShortHex(block?.blockHeader.nonce)
5653
: undefined;

frontend/src/components/elevator/elevator-ui.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { TipBlockResponse } from "../../service/type";
77
import { useAtomValue } from "jotai";
88
import { chainThemeAtom, ChainTheme } from "../../states/atoms";
99
import { useState } from "preact/hooks";
10-
import BlockModal from "./block-modal";
10+
import { compactToDifficulty, difficultyToEH } from "../../util/difficulty";
1111

1212
export interface ElevatorUIProp {
1313
block: TipBlockResponse;
@@ -42,8 +42,11 @@ export const ElevatorUI: FunctionalComponent<ElevatorUIProp> = ({
4242
>
4343
<div className={"w-1/5 self-end"}>
4444
<ElevatorMiner
45-
difficultyInHex={block.blockHeader.compact_target}
45+
difficultyInEH={difficultyToEH(
46+
compactToDifficulty(block.blockHeader.compact_target),
47+
)}
4648
doorClosing={isDoorClosing}
49+
nonce={block.blockHeader.nonce}
4750
/>
4851
</div>
4952

frontend/src/components/elevator/miner.tsx

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,20 @@ import { ChainTheme, chainThemeAtom } from "../../states/atoms";
44
import { Hex } from "@ckb-ccc/core";
55

66
export interface ElevatorUpButtonProps {
7-
difficultyInHex: Hex;
7+
difficultyInEH: number;
88
doorClosing: boolean;
9+
nonce: Hex;
910
}
1011

1112
const ElevatorMiner: FunctionComponent<ElevatorUpButtonProps> = ({
1213
doorClosing,
13-
difficultyInHex,
14+
difficultyInEH,
15+
nonce,
1416
}) => {
15-
const difficulty = parseFloat(
16-
(+difficultyInHex / 10000000000000000).toString(),
17-
).toFixed(2);
1817
const spinClass =
19-
+difficulty > 3.5
18+
difficultyInEH > 3.8
2019
? "animate-spin-fast"
21-
: +difficulty > 3.1
20+
: difficultyInEH > 3.5
2221
? "animate-spin-medium"
2322
: "animate-spin-slow";
2423
const chainTheme = useAtomValue(chainThemeAtom);
@@ -39,15 +38,12 @@ const ElevatorMiner: FunctionComponent<ElevatorUpButtonProps> = ({
3938
chainTheme === ChainTheme.mainnet
4039
? "/assets/svg/elevator/mainnet/miner-ape.svg"
4140
: "/assets/svg/elevator/testnet/miner-ape.svg";
41+
4242
return (
4343
<div>
44-
<div
45-
class={
46-
"flex flex-col h-full align-bottom items-center flex-grow"
47-
}
48-
>
44+
<div class="flex flex-col h-full align-bottom items-center flex-grow">
4945
<div
50-
className={`rounded-full ${doorClosing ? "bg-white" : `${bgBrand}`} w-[48px] h-[48px] flex justify-center align-center items-center`}
46+
className={`rounded-full ${doorClosing ? "bg-white" : `${bgBrand}`} w-[48px] h-[48px] flex justify-center items-center`}
5147
>
5248
<svg
5349
width="16"
@@ -63,34 +59,36 @@ const ElevatorMiner: FunctionComponent<ElevatorUpButtonProps> = ({
6359
</svg>
6460
</div>
6561

66-
<div className={`w-[10px] h-[20px] bg-black`} />
62+
<div className="w-[10px] h-[20px] bg-black" />
6763

6864
<div>
69-
<div className={"relative"}>
65+
<div className="relative">
7066
<img
71-
className={
72-
"absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2"
73-
}
67+
className="absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2"
7468
src={minerApe}
75-
alt=""
69+
alt="Miner Ape"
7670
/>
77-
7871
<img
7972
className={`${doorClosing ? "" : spinClass}`}
8073
src={minerWheel}
81-
alt=""
74+
alt="Miner Wheel"
8275
/>
76+
77+
{/* 气泡元素 */}
78+
{doorClosing && (
79+
<div
80+
className={`absolute left-[calc(70%+8px)] top-1/4 -translate-y-1/2 ${bgBrand} text-white px-3 py-1 rounded-full animate-bubble-up`}
81+
>
82+
Found a new nonce {nonce}
83+
</div>
84+
)}
8385
</div>
8486

85-
<div className={"relative"}>
86-
<div
87-
className={
88-
"absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2 text-text-inverse"
89-
}
90-
>
91-
<div>{difficulty} EH</div>
87+
<div className="relative">
88+
<div className="absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2 text-text-inverse">
89+
<div>Difficulty {difficultyInEH} EH</div>
9290
</div>
93-
<img src={minerBaseSvg} alt="" />
91+
<img src={minerBaseSvg} alt="Miner Base" />
9492
</div>
9593
</div>
9694
</div>

frontend/src/styles/animate.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,18 @@
9494
.animation-delay-1500 {
9595
animation-delay: 1500ms;
9696
}
97+
98+
@keyframes bubble-up {
99+
0% {
100+
transform: translateY(-50%) translateX(0);
101+
opacity: 1;
102+
}
103+
100% {
104+
transform: translateY(-150px) translateX(0);
105+
opacity: 0;
106+
}
107+
}
108+
109+
.animate-bubble-up {
110+
animation: bubble-up 3s ease-out forwards;
111+
}

frontend/src/util/difficulty.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* https://github.com/nervosnetwork/ckb/blob/develop/util/types/src/utilities/difficulty.rs
3+
*
4+
* 将 compact 格式的 u32 十六进制字符串转换为 difficulty 数值 (bigint).
5+
*
6+
* compact 格式为一个 32 位数,其中:
7+
* - 最高 8 位为 exponent(以 256 为底),
8+
* - 低 24 位为 mantissa,
9+
* 并且 target = mantissa * 256^(exponent - 3)(当 exponent > 3 时为左移,否则右移)。
10+
*
11+
* difficulty 的计算规则为:
12+
* - 如果 target 等于 1,则 difficulty = 2^256 - 1;
13+
* - 否则 difficulty = 2^256 / target。
14+
*
15+
* 如果 compact 格式不合法(overflow 或 target 为 0)则返回 0n。
16+
*
17+
* @param compactHex - u32 的 compact 格式,格式如 "0x1d00ffff" 或 "0x20800000"
18+
* @returns difficulty 数值(bigint)
19+
*/
20+
const HSPACE: bigint = 1n << 256n;
21+
const MAX_U256: bigint = (1n << 256n) - 1n;
22+
23+
export function compactToDifficulty(compactHex: string): bigint {
24+
// 将传入的十六进制字符串转换为 BigInt
25+
const compact: bigint = BigInt(compactHex);
26+
27+
// 提取 exponent(最高 8 位)
28+
const exponent: number = Number(compact >> 24n);
29+
30+
// 提取 mantissa(低 24 位)
31+
const mantissa: bigint = compact & 0x00ffffffn;
32+
33+
// 根据 exponent 计算 target
34+
let target: bigint;
35+
if (exponent <= 3) {
36+
target = mantissa >> (8n * BigInt(3 - exponent));
37+
} else {
38+
target = mantissa << (8n * BigInt(exponent - 3));
39+
}
40+
41+
// overflow 检查:如果 mantissa 非 0 且 exponent > 32,则视为 overflow,返回 0n
42+
if (mantissa !== 0n && exponent > 32) return 0n;
43+
if (target === 0n) return 0n;
44+
45+
// 计算 difficulty:如果 target 为 1,则返回 MAX_U256,否则返回 HSPACE / target
46+
return target === 1n ? MAX_U256 : HSPACE / target;
47+
}
48+
49+
export function difficultyToEH(value: bigint) {
50+
return +parseFloat(
51+
(+value.toString() / 1000000000000000000).toString(),
52+
).toFixed(2);
53+
}

0 commit comments

Comments
 (0)