@totemsdk/txpow 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/verify.d.ts +16 -0
- package/dist/verify.js +38 -0
- package/package.json +10 -10
- package/src/index.ts +1 -1
- package/src/verify.ts +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @totemsdk/txpow
|
|
2
|
+
|
|
3
|
+
**TxPoW — Minima's spam-prevention proof-of-work on every transaction.**
|
|
4
|
+
|
|
5
|
+
Every Minima transaction must carry a proof-of-work envelope before it can be broadcast. This package handles the full TxPoW lifecycle: serialize, mine, verify, and calibrate.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @totemsdk/txpow
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## What's inside
|
|
14
|
+
|
|
15
|
+
| Export | What it does |
|
|
16
|
+
|--------|-------------|
|
|
17
|
+
| `serializeTxHeader` / `serializeTxBody` / `serializeTxPoW` | Byte-identical TxPoW envelope assembly |
|
|
18
|
+
| `computeTxPoWId` | Derive the canonical TxPoW ID used for deduplication |
|
|
19
|
+
| `mineTxPoW(txBody, target)` | Local PoW mining loop — returns `{ minedHeaderBytes }` |
|
|
20
|
+
| `fetchTxPowTarget(axiaBaseUrl)` | Fetch current network difficulty from Axia |
|
|
21
|
+
| `verifyProofOfWork(txpowId, difficulty)` | Verify a received TxPoW (for relay nodes) |
|
|
22
|
+
| `calibrateHashRate()` | Benchmark local hardware hash rate |
|
|
23
|
+
| `estimateMiningCost(difficulty)` | Estimate mining time at current hash rate |
|
|
24
|
+
| `MAX_HASH`, `TX_POW_MIN_DIFFICULTY`, `CASCADE_LEVELS`, `MAIN_NET_CHAIN_ID` | Chain constants |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Mine and broadcast a transaction
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import {
|
|
32
|
+
mineTxPoW,
|
|
33
|
+
fetchTxPowTarget,
|
|
34
|
+
serializeTxPoW,
|
|
35
|
+
computeTxPoWId,
|
|
36
|
+
} from '@totemsdk/txpow';
|
|
37
|
+
|
|
38
|
+
// 1. Fetch current difficulty
|
|
39
|
+
const target = await fetchTxPowTarget('https://api.axia.to');
|
|
40
|
+
|
|
41
|
+
// 2. Mine (this runs the PoW loop — may take seconds)
|
|
42
|
+
const { minedHeaderBytes } = await mineTxPoW(txBodyBytes, target);
|
|
43
|
+
|
|
44
|
+
// 3. Assemble the final TxPoW envelope
|
|
45
|
+
const txpowBytes = serializeTxPoW(minedHeaderBytes, txBodyBytes);
|
|
46
|
+
const txpowId = computeTxPoWId(txpowBytes);
|
|
47
|
+
console.log('TxPoW ID:', txpowId);
|
|
48
|
+
|
|
49
|
+
// 4. Broadcast
|
|
50
|
+
await provider.broadcastTxPoW(Buffer.from(txpowBytes).toString('hex'));
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Verify received TxPoW
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { verifyProofOfWork } from '@totemsdk/txpow';
|
|
57
|
+
|
|
58
|
+
const ok = verifyProofOfWork(txpowId, difficulty);
|
|
59
|
+
if (!ok) throw new Error('Invalid proof-of-work');
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Calibrate hardware
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { calibrateHashRate, estimateMiningCost } from '@totemsdk/txpow';
|
|
66
|
+
|
|
67
|
+
const hashesPerSecond = await calibrateHashRate();
|
|
68
|
+
const estimatedMs = estimateMiningCost(targetDifficulty, hashesPerSecond);
|
|
69
|
+
console.log(`Expected mining time: ${estimatedMs}ms`);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## See also
|
|
73
|
+
|
|
74
|
+
- [`@totemsdk/tx-builder`](../tx-builder) — construct `txBodyBytes` before mining
|
|
75
|
+
- [`@totemsdk/node`](../node) — Node.js wallet that uses txpow internally
|
|
76
|
+
- [`@totemsdk/chain-provider`](../chain-provider) — `broadcastTxPoW` endpoint
|
package/dist/index.d.ts
CHANGED
|
@@ -22,5 +22,5 @@ export { MAX_HASH, ZERO_HASH, TX_POW_MIN_DIFFICULTY, CASCADE_LEVELS, MAIN_NET_CH
|
|
|
22
22
|
export { serializeMagic } from './magic.js';
|
|
23
23
|
export { mineTxPoW, isLessThan, type MineResult, type MineOptions, } from './mine.js';
|
|
24
24
|
export { fetchTxPowTarget, type TxPowParams } from './difficulty.js';
|
|
25
|
-
export { verifyProofOfWork, type VerifyResult } from './verify.js';
|
|
25
|
+
export { verifyProofOfWork, verifyTxPoWWork, type VerifyResult } from './verify.js';
|
|
26
26
|
export { calibrateHashRate, estimateMiningCost, type MiningEstimate, } from './calibrate.js';
|
package/dist/index.js
CHANGED
|
@@ -22,5 +22,5 @@ export { MAX_HASH, ZERO_HASH, TX_POW_MIN_DIFFICULTY, CASCADE_LEVELS, MAIN_NET_CH
|
|
|
22
22
|
export { serializeMagic } from './magic.js';
|
|
23
23
|
export { mineTxPoW, isLessThan, } from './mine.js';
|
|
24
24
|
export { fetchTxPowTarget } from './difficulty.js';
|
|
25
|
-
export { verifyProofOfWork } from './verify.js';
|
|
25
|
+
export { verifyProofOfWork, verifyTxPoWWork } from './verify.js';
|
|
26
26
|
export { calibrateHashRate, estimateMiningCost, } from './calibrate.js';
|
package/dist/verify.d.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* verifyProofOfWork checks that a given txpowId beats the difficulty target
|
|
5
5
|
* without re-mining. Used by relay nodes to reject under-work submissions.
|
|
6
|
+
*
|
|
7
|
+
* verifyTxPoWWork is a convenience wrapper for relay nodes that receive raw
|
|
8
|
+
* TxPoW hex: it hashes the full bytes as a proxy txpowId and checks against
|
|
9
|
+
* the network minimum difficulty. PureMinima performs authoritative validation
|
|
10
|
+
* on submission; this is a first-pass spam filter.
|
|
6
11
|
*/
|
|
7
12
|
export interface VerifyResult {
|
|
8
13
|
valid: boolean;
|
|
@@ -19,3 +24,14 @@ export interface VerifyResult {
|
|
|
19
24
|
* @param mTxnDifficulty The 32-byte difficulty target from the TxBody.
|
|
20
25
|
*/
|
|
21
26
|
export declare function verifyProofOfWork(txpowId: Uint8Array, mTxnDifficulty: Uint8Array): VerifyResult;
|
|
27
|
+
/**
|
|
28
|
+
* Relay-side work verification from raw TxPoW hex.
|
|
29
|
+
*
|
|
30
|
+
* Validates hex format, minimum length (spam filter), then computes
|
|
31
|
+
* SHA3-256 of the serialized bytes as a proxy txpowId and checks it
|
|
32
|
+
* against TX_POW_MIN_DIFFICULTY.
|
|
33
|
+
*
|
|
34
|
+
* Note: full structural parsing of TxPoW headers is deferred to a future
|
|
35
|
+
* parser. PureMinima performs authoritative work verification on submission.
|
|
36
|
+
*/
|
|
37
|
+
export declare function verifyTxPoWWork(txpowHex: string): VerifyResult;
|
package/dist/verify.js
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* verifyProofOfWork checks that a given txpowId beats the difficulty target
|
|
5
5
|
* without re-mining. Used by relay nodes to reject under-work submissions.
|
|
6
|
+
*
|
|
7
|
+
* verifyTxPoWWork is a convenience wrapper for relay nodes that receive raw
|
|
8
|
+
* TxPoW hex: it hashes the full bytes as a proxy txpowId and checks against
|
|
9
|
+
* the network minimum difficulty. PureMinima performs authoritative validation
|
|
10
|
+
* on submission; this is a first-pass spam filter.
|
|
6
11
|
*/
|
|
7
12
|
import { isLessThan } from './mine.js';
|
|
13
|
+
import { computeTxPoWId } from './serialization.js';
|
|
14
|
+
import { TX_POW_MIN_DIFFICULTY } from './constants.js';
|
|
8
15
|
function toHex(bytes) {
|
|
9
16
|
return '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
10
17
|
}
|
|
@@ -41,3 +48,34 @@ export function verifyProofOfWork(txpowId, mTxnDifficulty) {
|
|
|
41
48
|
reason: valid ? undefined : 'txpowId ≥ mTxnDifficulty',
|
|
42
49
|
};
|
|
43
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Relay-side work verification from raw TxPoW hex.
|
|
53
|
+
*
|
|
54
|
+
* Validates hex format, minimum length (spam filter), then computes
|
|
55
|
+
* SHA3-256 of the serialized bytes as a proxy txpowId and checks it
|
|
56
|
+
* against TX_POW_MIN_DIFFICULTY.
|
|
57
|
+
*
|
|
58
|
+
* Note: full structural parsing of TxPoW headers is deferred to a future
|
|
59
|
+
* parser. PureMinima performs authoritative work verification on submission.
|
|
60
|
+
*/
|
|
61
|
+
export function verifyTxPoWWork(txpowHex) {
|
|
62
|
+
if (typeof txpowHex !== 'string' || !/^[0-9a-fA-F]+$/.test(txpowHex)) {
|
|
63
|
+
return {
|
|
64
|
+
valid: false,
|
|
65
|
+
txpowId: '',
|
|
66
|
+
difficulty: toHex(TX_POW_MIN_DIFFICULTY),
|
|
67
|
+
reason: 'invalid hex encoding',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (txpowHex.length < 200) {
|
|
71
|
+
return {
|
|
72
|
+
valid: false,
|
|
73
|
+
txpowId: '',
|
|
74
|
+
difficulty: toHex(TX_POW_MIN_DIFFICULTY),
|
|
75
|
+
reason: 'TxPoW too short (spam filter: minimum 100 bytes)',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const bytes = Uint8Array.from(Buffer.from(txpowHex, 'hex'));
|
|
79
|
+
const proxyId = computeTxPoWId(bytes);
|
|
80
|
+
return verifyProofOfWork(proxyId, TX_POW_MIN_DIFFICULTY);
|
|
81
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/txpow",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "TxPoW envelope serialization and proof-of-work mining for the Minima protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,20 +11,15 @@
|
|
|
11
11
|
"import": "./dist/index.js"
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"clean": "rm -rf dist",
|
|
17
|
-
"test": "jest --passWithNoTests"
|
|
18
|
-
},
|
|
19
14
|
"files": [
|
|
20
15
|
"dist",
|
|
21
16
|
"src"
|
|
22
17
|
],
|
|
23
18
|
"dependencies": {
|
|
24
|
-
"@totemsdk/core": "
|
|
19
|
+
"@totemsdk/core": "file:../core"
|
|
25
20
|
},
|
|
26
21
|
"peerDependencies": {
|
|
27
|
-
"@noble/hashes": ">=1.3.0"
|
|
22
|
+
"@noble/hashes": ">=1.3.0 <2.0.0"
|
|
28
23
|
},
|
|
29
24
|
"peerDependenciesMeta": {
|
|
30
25
|
"@noble/hashes": {
|
|
@@ -45,5 +40,10 @@
|
|
|
45
40
|
"publishConfig": {
|
|
46
41
|
"access": "public"
|
|
47
42
|
},
|
|
48
|
-
"license": "MIT"
|
|
49
|
-
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc",
|
|
46
|
+
"clean": "rm -rf dist",
|
|
47
|
+
"test": "jest --passWithNoTests"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -47,7 +47,7 @@ export {
|
|
|
47
47
|
|
|
48
48
|
export { fetchTxPowTarget, type TxPowParams } from './difficulty.js';
|
|
49
49
|
|
|
50
|
-
export { verifyProofOfWork, type VerifyResult } from './verify.js';
|
|
50
|
+
export { verifyProofOfWork, verifyTxPoWWork, type VerifyResult } from './verify.js';
|
|
51
51
|
|
|
52
52
|
export {
|
|
53
53
|
calibrateHashRate,
|
package/src/verify.ts
CHANGED
|
@@ -3,9 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* verifyProofOfWork checks that a given txpowId beats the difficulty target
|
|
5
5
|
* without re-mining. Used by relay nodes to reject under-work submissions.
|
|
6
|
+
*
|
|
7
|
+
* verifyTxPoWWork is a convenience wrapper for relay nodes that receive raw
|
|
8
|
+
* TxPoW hex: it hashes the full bytes as a proxy txpowId and checks against
|
|
9
|
+
* the network minimum difficulty. PureMinima performs authoritative validation
|
|
10
|
+
* on submission; this is a first-pass spam filter.
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
import { isLessThan } from './mine.js';
|
|
14
|
+
import { computeTxPoWId } from './serialization.js';
|
|
15
|
+
import { TX_POW_MIN_DIFFICULTY } from './constants.js';
|
|
9
16
|
|
|
10
17
|
export interface VerifyResult {
|
|
11
18
|
valid: boolean;
|
|
@@ -55,3 +62,35 @@ export function verifyProofOfWork(
|
|
|
55
62
|
reason: valid ? undefined : 'txpowId ≥ mTxnDifficulty',
|
|
56
63
|
};
|
|
57
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Relay-side work verification from raw TxPoW hex.
|
|
68
|
+
*
|
|
69
|
+
* Validates hex format, minimum length (spam filter), then computes
|
|
70
|
+
* SHA3-256 of the serialized bytes as a proxy txpowId and checks it
|
|
71
|
+
* against TX_POW_MIN_DIFFICULTY.
|
|
72
|
+
*
|
|
73
|
+
* Note: full structural parsing of TxPoW headers is deferred to a future
|
|
74
|
+
* parser. PureMinima performs authoritative work verification on submission.
|
|
75
|
+
*/
|
|
76
|
+
export function verifyTxPoWWork(txpowHex: string): VerifyResult {
|
|
77
|
+
if (typeof txpowHex !== 'string' || !/^[0-9a-fA-F]+$/.test(txpowHex)) {
|
|
78
|
+
return {
|
|
79
|
+
valid: false,
|
|
80
|
+
txpowId: '',
|
|
81
|
+
difficulty: toHex(TX_POW_MIN_DIFFICULTY),
|
|
82
|
+
reason: 'invalid hex encoding',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (txpowHex.length < 200) {
|
|
86
|
+
return {
|
|
87
|
+
valid: false,
|
|
88
|
+
txpowId: '',
|
|
89
|
+
difficulty: toHex(TX_POW_MIN_DIFFICULTY),
|
|
90
|
+
reason: 'TxPoW too short (spam filter: minimum 100 bytes)',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const bytes = Uint8Array.from(Buffer.from(txpowHex, 'hex'));
|
|
94
|
+
const proxyId = computeTxPoWId(bytes);
|
|
95
|
+
return verifyProofOfWork(proxyId, TX_POW_MIN_DIFFICULTY);
|
|
96
|
+
}
|