rebrand(lethean): update branding, ports, and config for Lethean blockchain
Some checks failed
Publish lethean-utils-js / build-and-publish (push) Failing after 57s

- Coin: Zano → Lethean, ticker: ZAN/ZANO → LTHN
- Ports: 11211 → 36941 (mainnet RPC), 46941 (testnet RPC)
- Wallet: 11212 → 36944/46944
- Address prefix: iTHN
- URLs: zano.org → lethean.io
- Explorer links: explorer.lthn.io

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-01 22:24:07 +01:00
parent b69407351c
commit 0bb6edf8f1
No known key found for this signature in database
GPG key ID: AF404715446AEB41
21 changed files with 10392 additions and 951 deletions

View file

@ -1,4 +1,4 @@
name: Publish zano-utils-js to zano-project
name: Publish lethean-utils-js
on:
push:

18
Dockerfile Normal file
View file

@ -0,0 +1,18 @@
# Build-only: compiles the TypeScript crypto library to dist/
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build
# Minimal image with the compiled library
FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Zano Team
Copyright (c) 2025 Lethean Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,7 +1,7 @@
# zano-utils-js
Set of helpers and tools for JS developers that working with Zano
# lethean-utils-js
Set of helpers and tools for JS developers working with the Lethean blockchain.
<!-- TOC -->
* [Zano Utils JS](#zano-utils-js)
* [Lethean Utils JS](#lethean-utils-js)
* [Blockchain Description](#blockchain-description)
* [Functions](#functions)
* [decodeTransaction](#decodetransaction)
@ -32,8 +32,8 @@ Set of helpers and tools for JS developers that working with Zano
## Blockchain Description
- **Ticker**: Zano
- **Network**: Zano
- **Ticker**: LTHN
- **Network**: Lethean
---
@ -50,7 +50,7 @@ Set of helpers and tools for JS developers that working with Zano
curl -X POST \
-H "Content-Type: application/json" \
--data '{ "jsonrpc": "2.0", "method": "get_tx_details", "params": {"tx_hash": "77b09d759fefd512642f9a5e4e31ed0fefbaf1a8e602a2be94fc511ff982f7cf" }, "id": 1 }' \
"http://37.27.100.59:10500/json_rpc"
"http://127.0.0.1:36941/json_rpc"
```
<details>
@ -144,8 +144,8 @@ You can prodive integrated address or master address.
#### Import and usage
```ts
import { decodeTransaction } from '@zano-project/zano-utils-js';
import type { DecodeTransactionResult } from '@zano-project/zano-utils-js';
import { decodeTransaction } from '@aspect-build/lethean-utils-js';
import type { DecodeTransactionResult } from '@aspect-build/lethean-utils-js';
decodeTransaction(objectInJson, secretViewKey, address);
// or provide public spend key
@ -162,13 +162,13 @@ type DecodeTransactionResult =
### `txUtils`
Namespace that provides low-level cryptographic and transaction-related helper functions for Zano transactions.
Namespace that provides low-level cryptographic and transaction-related helper functions for Lethean transactions.
These utilities allow working with transaction outputs, stealth addresses, key images, and encrypted fields.
Import utils:
```ts
import { txUtils } from '@zano-project/zano-utils-js';
import { txUtils } from '@aspect-build/lethean-utils-js';
```
#### `getConcealingPoint`
@ -245,8 +245,8 @@ parseObjectInJson(objectInJson: string): TransactionObject | TransactionObjectV3
### `generateAccount`
```typescript
import { generateAccount } from '@zano-project/zano-utils-js';
import type { AccountResult } from '@zano-project/zano-utils-js';
import { generateAccount } from '@aspect-build/lethean-utils-js';
import type { AccountResult } from '@aspect-build/lethean-utils-js';
const account: AccountResult = generateAccount();
```
@ -270,10 +270,10 @@ type AccountKeys = {
### `validateAccount`
```ts
import { validateAccount } from '@zano-project/zano-utils-js';
import { validateAccount } from '@aspect-build/lethean-utils-js';
const validatedAccount: boolean = validateAccount(
'ZxC15vh38qHAZbfsUXTpxoiyeLhavbBzsQQk81fEwP4jYxN4qR8SEhMfXkRBpQw6vbbSEGpK2VPVPADnL6h3ZViL29Remh4oH',
'iTHNLmFJabSdqSdpjs6QkSRFVTkDgsQWMCTzMYZM7QuRKrnEumCNE3PbwbDtVVRwroSZPjx98wx7ZLmqY1LzWYuE2j43qDr11t',
'21dcd98fb9dc392aeabe1d5cfb90faf63840685762448bf49a48d58d0c70bf0b',
'2ff9e77456d0e65b50d80392a098cddf9032744bd876371fffe95476a92d8564',
'88609e3bc954fe8b5f1a5f0a7e7e44528835b62890de49000033b28898888d01',
@ -297,8 +297,8 @@ async function validateAccount(
### `generateAccountKeys`
```ts
import { generateAccountKeys } from '@zano-project/zano-utils-js';
import type { AccountKeys } from '@zano-project/zano-utils-js';
import { generateAccountKeys } from '@aspect-build/lethean-utils-js';
import type { AccountKeys } from '@aspect-build/lethean-utils-js';
const accountKeys: AccountKeys = generateAccountKeys();
```
@ -317,7 +317,7 @@ type AccountKeys = {
### `privateKeyToPublicKey`
```ts
import { privateKeyToPublicKey } from '@zano-project/zano-utils-js';
import { privateKeyToPublicKey } from '@aspect-build/lethean-utils-js';
const publicKey: string = privateKeyToPublicKey('88609e3bc954fe8b5f1a5f0a7e7e44528835b62890de49000033b28898888d01');
```
@ -326,8 +326,8 @@ const publicKey: string = privateKeyToPublicKey('88609e3bc954fe8b5f1a5f0a7e7e445
### `getAccountBySecretSpendKey`
```ts
import { getAccountBySecretSpendKey } from '@zano-project/zano-utils-js';
import type { AccountKeys } from '@zano-project/zano-utils-js';
import { getAccountBySecretSpendKey } from '@aspect-build/lethean-utils-js';
import type { AccountKeys } from '@aspect-build/lethean-utils-js';
const accountKeys: AccountKeys = getAccountBySecretSpendKey('88609e3bc954fe8b5f1a5f0a7e7e44528835b62890de49000033b28898888d01');
```
@ -347,8 +347,8 @@ type AccountKeys = {
### `getKeyPair`
```ts
import { getKeyPair } from '@zano-project/zano-utils-js';
import type { KeyPair } from '@zano-project/zano-utils-js';
import { getKeyPair } from '@aspect-build/lethean-utils-js';
import type { KeyPair } from '@aspect-build/lethean-utils-js';
const keypair: KeyPair = getKeyPair();
```
@ -369,18 +369,18 @@ type KeyPair = {
The function accepts either the main master address or the integrated address as a parameter.
```ts
import { getIntegratedAddress } from '@zano-project/zano-utils-js';
import { getIntegratedAddress } from '@aspect-build/lethean-utils-js';
const integratedAddress: string = getIntegratedAddress('ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH');
const integratedAddress: string = getIntegratedAddress('iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi');
```
---
### `createIntegratedAddress`
```ts
import { createIntegratedAddress } from '@zano-project/zano-utils-js';
import { createIntegratedAddress } from '@aspect-build/lethean-utils-js';
const integratedAddress: string = createIntegratedAddress('ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH', '49c925855b863a25');
const integratedAddress: string = createIntegratedAddress('iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi', '49c925855b863a25');
```
---
@ -392,7 +392,7 @@ params:
2. viewPublicKey: string
```ts
import { getMasterAddress } from '@zano-project/zano-utils-js';
import { getMasterAddress } from '@aspect-build/lethean-utils-js';
const integratedAddress: string = getMasterAddress('9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa', 'a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3');
```
@ -405,10 +405,10 @@ params:
1. integratedAddress: string,
```ts
import { splitIntegratedAddress } from '@zano-project/zano-utils-js';
import type { SplitedIntegratedAddress } from '@zano-project/zano-utils-js';
import { splitIntegratedAddress } from '@aspect-build/lethean-utils-js';
import type { SplitedIntegratedAddress } from '@aspect-build/lethean-utils-js';
const integratedAddress: SplitedIntegratedAddress = splitIntegratedAddress('iZ2kFmwxRHoaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e47JPoHxGL5UU2D1tpQMg4');
const integratedAddress: SplitedIntegratedAddress = splitIntegratedAddress('iTHnVGr7tms9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJQSA6PNs1faBfevEK7');
```
#### Returned data splitIntegratedAddress
@ -428,10 +428,10 @@ params:
1. address: string,
```ts
import { getKeysFromAddress } from '@zano-project/zano-utils-js';
import type { ZarcanumAddressKeys } from '@zano-project/zano-utils-js';
import { getKeysFromAddress } from '@aspect-build/lethean-utils-js';
import type { ZarcanumAddressKeys } from '@aspect-build/lethean-utils-js';
const integratedAddress: ZarcanumAddressKeys = getKeysFromAddress('iZ2kFmwxRHoaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e47JPoHxGL5UU2D1tpQMg4');
const integratedAddress: ZarcanumAddressKeys = getKeysFromAddress('iTHnVGr7tms9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJQSA6PNs1faBfevEK7');
```
#### Returned data getKeysFromAddress
@ -448,7 +448,7 @@ type ZarcanumAddressKeys = {
Descr: generate payment id for creating integrated address
```ts
import { generatePaymentId } from '@zano-project/zano-utils-js';
import { generatePaymentId } from '@aspect-build/lethean-utils-js';
const paymentId: string = generatePaymentId();
```
@ -466,8 +466,8 @@ const paymentId: string = generatePaymentId();
@returns The secret spend key as a hex string, or `false` if parsing failed.
```ts
import { mnemonicToSeed } from '@zano-project/zano-utils-js';
import type { MnemonicToSeedResult } from '@zano-project/zano-utils-js';
import { mnemonicToSeed } from '@aspect-build/lethean-utils-js';
import type { MnemonicToSeedResult } from '@aspect-build/lethean-utils-js';
const secretSpendKey: MnemonicToSeedResult = mnemonicToSeed('bridge passion scale vast speak mud murder own birthday flight always hair especially tickle crowd shatter tickle deserve hopefully bomb join plan darling aunt beneath give');
```
@ -492,8 +492,8 @@ type MnemonicToSeedResult = string | false;
- **No audit flag support**: The library does not yet support the audit flag feature.
```ts
import { seedToMnemonic } from '@zano-project/zano-utils-js';
import type { SeedToMnemonicResult } from '@zano-project/zano-utils-js';
import { seedToMnemonic } from '@aspect-build/lethean-utils-js';
import type { SeedToMnemonicResult } from '@aspect-build/lethean-utils-js';
const randomBytes: string = getRandomBytes(64).toString('hex');
@ -510,7 +510,7 @@ type SeedToMnemonicResult = string;
# Supporting project/donations
---
ZANO @dev <br>
LTHN @dev <br>
BTC bc1qpa8w8eaehlplfepmnzpd7v9j046899nktxnkxp <br>
BCH qqgq078vww5exd9kt3frx6krdyznmp80hcygzlgqzd <br>
ETH 0x206c52b78141498e74FF074301ea90888C40c178 <br>

9341
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,9 @@
{
"name": "@zano-project/zano-utils-js",
"version": "0.0.4",
"repository": "https://github.com/hyle-team/zano-utils-js",
"author": "Zano Team",
"name": "@aspect-build/lethean-utils-js",
"version": "0.1.0",
"description": "Cryptographic utilities for the Lethean blockchain (address encoding, transaction decoding, mnemonic seeds)",
"repository": "https://forge.lthn.ai/lthn/lethean-utils-js",
"author": "Lethean Team",
"license": "MIT",
"publishConfig": {
"access": "public"

View file

@ -2,21 +2,21 @@ import { ZarcanumAddressKeys } from 'src/decode/types';
import {
PAYMENT_ID_REGEX,
ADDRESS_TAG_PREFIX,
ADDRESS_FLAG_PREFIX,
ADDRESS_PREFIX,
INTEGRATED_ADDRESS_PREFIX,
BUFFER_INTEGRATED_ADDRESS_LENGTH,
INTEGRATED_ADDRESS_REGEX,
PAYMENT_ID_LENGTH,
INTEGRATED_ADDRESS_FLAG_PREFIX,
INTEGRATED_ADDRESS_TAG_PREFIX,
BUFFER_ADDRESS_LENGTH,
CHECKSUM_LENGTH,
FLAG_LENGTH,
SPEND_KEY_LENGTH,
TAG_LENGTH,
VIEW_KEY_LENGTH,
ADDRESS_REGEX,
ACCOUNT_KEY_REGEX,
encodeVarint,
decodeVarint,
ADDRESS_PREFIX_LENGTH,
INTEGRATED_PREFIX_LENGTH,
} from './constants';
import { DecodedAddress, SplitedIntegratedAddress } from './types';
import { base58Encode, base58Decode } from '../core/base58';
@ -52,16 +52,14 @@ function createIntegratedAddress(address: string, paymentId: string): string {
function formatIntegratedAddress(addressDecoded: DecodedAddress, paymentIdBuffer: Buffer): string {
const {
tag,
flag,
viewPublicKey,
spendPublicKey,
viewPublicKey,
}: DecodedAddress = addressDecoded;
const integratedAddressBuffer: Buffer = Buffer.concat([
Buffer.from([tag, flag]),
viewPublicKey,
encodeVarint(INTEGRATED_ADDRESS_PREFIX),
spendPublicKey,
viewPublicKey,
paymentIdBuffer,
]);
@ -76,31 +74,27 @@ function decodeAddress(address: string): DecodedAddress {
throw new Error('Invalid decode address');
}
let offset = TAG_LENGTH + FLAG_LENGTH;
const viewPublicKey: Buffer = decodedAddress.subarray(offset, offset + VIEW_KEY_LENGTH);
offset += VIEW_KEY_LENGTH;
const { value: prefix, length: prefixLength } = decodeVarint(decodedAddress);
let offset = prefixLength;
const spendPublicKey: Buffer = decodedAddress.subarray(offset, offset + SPEND_KEY_LENGTH);
offset += SPEND_KEY_LENGTH;
const viewPublicKey: Buffer = decodedAddress.subarray(offset, offset + VIEW_KEY_LENGTH);
return {
tag: INTEGRATED_ADDRESS_TAG_PREFIX,
flag: INTEGRATED_ADDRESS_FLAG_PREFIX,
viewPublicKey,
prefix,
spendPublicKey,
viewPublicKey,
};
} catch (error) {
throw new Error(`Error decode address: ${error.message}`);
}
}
function encodeAddress(tag: number, flag: number, spendPublicKey: string, viewPublicKey: string): string {
function encodeAddress(prefix: number, spendPublicKey: string, viewPublicKey: string): string {
try {
if (tag < 0) {
throw new Error('Invalid tag');
if (prefix < 0) {
throw new Error('Invalid prefix');
}
if (flag < 0) {
throw new Error('Invalid flag');
}
let buf: Buffer = Buffer.from([tag, flag]);
if (spendPublicKey.length !== 64 && !ACCOUNT_KEY_REGEX.test(spendPublicKey)) {
throw new Error('Invalid spendPublicKey: must be a hexadecimal string with a length of 64');
@ -112,7 +106,7 @@ function encodeAddress(tag: number, flag: number, spendPublicKey: string, viewPu
}
const viewKey: Buffer = Buffer.from(viewPublicKey, 'hex');
buf = Buffer.concat([buf, spendKey, viewKey]);
let buf: Buffer = Buffer.concat([encodeVarint(prefix), spendKey, viewKey]);
const hash: string = getChecksum(buf);
return base58Encode(Buffer.concat([buf, Buffer.from(hash, 'hex')]));
@ -123,9 +117,6 @@ function encodeAddress(tag: number, flag: number, spendPublicKey: string, viewPu
function getMasterAddress(spendPublicKey: string, viewPublicKey: string): string {
try {
const tag: number = ADDRESS_TAG_PREFIX;
const flag: number = ADDRESS_FLAG_PREFIX;
if (spendPublicKey.length !== 64 && !ACCOUNT_KEY_REGEX.test(spendPublicKey)) {
throw new Error('Invalid spendPublicKey: must be a hexadecimal string with a length of 64');
}
@ -134,12 +125,10 @@ function getMasterAddress(spendPublicKey: string, viewPublicKey: string): string
throw new Error('Invalid viewPrivateKey: must be a hexadecimal string with a length of 64');
}
const viewPublicKeyBuf: Buffer = Buffer.from(viewPublicKey, 'hex');
const spendPublicKeyBuf: Buffer = Buffer.from(spendPublicKey, 'hex');
const viewPublicKeyBuf: Buffer = Buffer.from(viewPublicKey, 'hex');
let buf: Buffer = Buffer.from([tag, flag]);
buf = Buffer.concat([buf, spendPublicKeyBuf, viewPublicKeyBuf]);
let buf: Buffer = Buffer.concat([encodeVarint(ADDRESS_PREFIX), spendPublicKeyBuf, viewPublicKeyBuf]);
const hash: string = getChecksum(buf);
return base58Encode(Buffer.concat([buf, Buffer.from(hash, 'hex')]));
@ -163,7 +152,8 @@ function splitIntegratedAddress(integratedAddress: string): SplitedIntegratedAdd
throw new Error('spendPublicKey or viewPublicKey are missing');
}
const paymentId: string = base58Decode(integratedAddress).subarray(66, 66 + PAYMENT_ID_LENGTH).toString('hex');
const paymentIdOffset: number = INTEGRATED_PREFIX_LENGTH + SPEND_KEY_LENGTH + VIEW_KEY_LENGTH;
const paymentId: string = base58Decode(integratedAddress).subarray(paymentIdOffset, paymentIdOffset + PAYMENT_ID_LENGTH).toString('hex');
const masterAddress: string = getMasterAddress(spendPublicKey, viewPublicKey);
return {
@ -178,14 +168,14 @@ function splitIntegratedAddress(integratedAddress: string): SplitedIntegratedAdd
/*
* Retrieves public spend and view keys from the Zano address.
* Retrieves public spend and view keys from the Lethean address.
*
* This function decodes a Zano address and Integrated address from its Base58 representation and extracts
* This function decodes a Lethean address and Integrated address from its Base58 representation and extracts
* the spend and view keys contained within it. If the address is not in a valid
* Base58 format, or if the resulting buffer does not conform to expected length specifics,
* an error is thrown.
*
* @param {string} address - A Zano address and Integrated address in Base58 format.
* @param {string} address - A Lethean address and Integrated address in Base58 format.
* @returns { ZarcanumAddressKeys } An object containing the spend and view keys.
* @throws { Error } Throws an error if the address format or buffer length is invalid.
*/
@ -207,15 +197,17 @@ function getKeysFromAddress(address: string): ZarcanumAddressKeys {
throw new Error('Invalid address checksum');
}
const { length: prefixLength } = decodeVarint(buf);
const spendPublicKey: string = Buffer.from(
buf.buffer,
TAG_LENGTH + FLAG_LENGTH,
prefixLength,
SPEND_KEY_LENGTH,
).toString('hex');
const viewPublicKey: string = Buffer.from(
buf.buffer,
TAG_LENGTH + FLAG_LENGTH + SPEND_KEY_LENGTH,
prefixLength + SPEND_KEY_LENGTH,
VIEW_KEY_LENGTH,
).toString('hex');

View file

@ -1,23 +1,55 @@
export const TAG_LENGTH = 1;
export const FLAG_LENGTH = 1;
export const SPEND_KEY_LENGTH = 32;
export const VIEW_KEY_LENGTH = 32;
export const CHECKSUM_LENGTH = 4;
// Lethean address prefix as a varint integer.
// Standard addresses start with 'iTHN', integrated with 'iTHn'.
// See blockchain/src/config/currency_config.h for canonical values.
export const ADDRESS_PREFIX = 0x1eaf7;
export const INTEGRATED_ADDRESS_PREFIX = 0xdeaf7;
// Varint-encode a prefix integer into a Buffer.
export function encodeVarint(value: number): Buffer {
const bytes: number[] = [];
while (value >= 0x80) {
bytes.push((value & 0x7f) | 0x80);
value >>>= 7;
}
bytes.push(value);
return Buffer.from(bytes);
}
// Decode a varint from the start of a buffer, returning the value and byte length consumed.
export function decodeVarint(buf: Buffer): { value: number; length: number } {
let value = 0;
let shift = 0;
let length = 0;
let byte: number;
do {
byte = buf[length];
value |= (byte & 0x7f) << shift;
shift += 7;
length++;
} while ((byte & 0x80) !== 0);
return { value, length };
}
export const ADDRESS_PREFIX_LENGTH: number = encodeVarint(ADDRESS_PREFIX).length;
export const INTEGRATED_PREFIX_LENGTH: number = encodeVarint(INTEGRATED_ADDRESS_PREFIX).length;
export const BUFFER_ADDRESS_LENGTH: number =
TAG_LENGTH +
FLAG_LENGTH +
ADDRESS_PREFIX_LENGTH +
SPEND_KEY_LENGTH +
VIEW_KEY_LENGTH +
CHECKSUM_LENGTH;
export const ADDRESS_REGEX = /^Z[a-zA-Z0-9]{96}$/;
export const INTEGRATED_ADDRESS_REGEX = /^iZ[a-zA-Z0-9]{106}$/;
export const ADDRESS_REGEX = /^iTHN[a-zA-Z0-9]{94}$/;
export const INTEGRATED_ADDRESS_REGEX = /^iTHn[a-zA-Z0-9]{105}$/;
export const PAYMENT_ID_LENGTH = 8;
export const INTEGRATED_ADDRESS_FLAG_PREFIX = 0x6c;
export const INTEGRATED_ADDRESS_TAG_PREFIX = 0xf8;
export const ADDRESS_FLAG_PREFIX = 0x01;
export const ADDRESS_TAG_PREFIX = 0xC5;
export const BUFFER_INTEGRATED_ADDRESS_LENGTH =
BUFFER_ADDRESS_LENGTH +
PAYMENT_ID_LENGTH;
export const BUFFER_INTEGRATED_ADDRESS_LENGTH: number =
INTEGRATED_PREFIX_LENGTH +
SPEND_KEY_LENGTH +
VIEW_KEY_LENGTH +
PAYMENT_ID_LENGTH +
CHECKSUM_LENGTH;
export const PAYMENT_ID_REGEX = /^[a-fA-F0-9]{16}$/;
export const ACCOUNT_KEY_REGEX = /^([0-9a-fA-F]{2})+$/;

View file

@ -4,10 +4,9 @@ export type ZarcanumAddressKeys = {
}
export type DecodedAddress = {
tag: number;
flag: number;
viewPublicKey: Buffer;
prefix: number;
spendPublicKey: Buffer;
viewPublicKey: Buffer;
}
export type SplitedIntegratedAddress = {

View file

@ -35,7 +35,7 @@ export class BinaryArchive {
/**
*
* @see https://github.com/hyle-team/zano/blob/69a5d42d9908b7168247e103b2b40aae8c1fb3f5/src/common/varint.h#L59
* @see Upstream ref: hyle-team/zano@69a5d42 src/common/varint.h#L59
*/
readVarint(): bigint {
let varint = 0n;

View file

@ -9,22 +9,22 @@ const { red } = ec.curve;
export const A = new BN(486662, 10).toRed(red);
// sqrt(-1)
// https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-ops-data.c#L12
// Upstream ref: hyle-team/zano@2817090 src/crypto/crypto-ops-data.c#L12
//export const sqrtm1 = new BN(1).toRed(red).redNeg().redSqrt();
export const sqrtm1: RedBN = new BN('547cdb7fb03e20f4d4b2ff66c2042858d0bce7f952d01b873b11e4d8b5f15f3d', 'hex').toRed(red);
// sqrt(-2 * A * (A + 2))
// https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-ops-data.c#L843
// Upstream ref: hyle-team/zano@2817090 src/crypto/crypto-ops-data.c#L843
export const fffb1: RedBN = new BN('7e71fbefdad61b1720a9c53741fb19e3d19404a8b92a738d22a76975321c41ee', 'hex').toRed(red);
// sqrt(2 * A * (A + 2))
// https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-ops-data.c#L844
// Upstream ref: hyle-team/zano@2817090 src/crypto/crypto-ops-data.c#L844
export const fffb2: RedBN = new BN('32f9e1f5fba5d3096e2bae483fe9a041ae21fcb9fba908202d219b7c9f83650d', 'hex').toRed(red);
// sqrt(-sqrt(-1) * A * (A + 2))
// https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-ops-data.c#L845
// Upstream ref: hyle-team/zano@2817090 src/crypto/crypto-ops-data.c#L845
export const fffb3: RedBN = new BN('1a43f3031067dbf926c0f4887ef7432eee46fc08a13f4a49853d1903b6b39186', 'hex').toRed(red);
// sqrt(sqrt(-1) * A * (A + 2))
// https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-ops-data.c#L846
// Upstream ref: hyle-team/zano@2817090 src/crypto/crypto-ops-data.c#L846
export const fffb4: RedBN = new BN('674a110d14c208efb89546403f0da2ed4024ff4ea5964229581b7d8717302c66', 'hex').toRed(red);

View file

@ -57,7 +57,7 @@ export function getDerivationToScalar(txPubKey: string, secViewKey: string, outI
/*
* out.concealing_point = (crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = 1/8 * Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/currency_core/currency_format_utils.cpp#L1270
* Upstream ref: hyle-team/zano@2817090src/currency_core/currency_format_utils.cpp#L1270
*/
export function calculateConcealingPoint(Hs: Buffer, pubViewKeyBuff: Buffer): Buffer {
const scalar: BN = decodeScalar(Hs, 'Invalid sсalar');
@ -68,7 +68,7 @@ export function calculateConcealingPoint(Hs: Buffer, pubViewKeyBuff: Buffer): Bu
/*
* out.blinded_asset_id = (crypto::c_scalar_1div8 * blinded_asset_id).to_public_key(); // T = 1/8 * (H_asset + s * X)
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/currency_core/currency_format_utils.cpp#L1278
* Upstream ref: hyle-team/zano@2817090src/currency_core/currency_format_utils.cpp#L1278
*/
export function calculateBlindedAssetId(Hs: Buffer, assetId: Buffer, X: Buffer): Buffer {
const assetIdCopy: Buffer = Buffer.from(assetId);
@ -88,11 +88,11 @@ export function calculateBlindedAssetId(Hs: Buffer, assetId: Buffer, X: Buffer):
}
// todo: crypto::point_t asset_id = blinded_asset_id - asset_id_blinding_mask * crypto::c_point_X; // H = T - s * X
// https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/currency_core/currency_format_utils.cpp#L3289
// Upstream ref: hyle-team/zano@2817090src/currency_core/currency_format_utils.cpp#L3289
/*
* generate_key_derivation
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L175
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L175
*/
export function generateKeyDerivation(txPubKey: Buffer, secKeyView: Buffer): Buffer {
const s: BN = decodeScalar(secKeyView, 'Invalid secret key');
@ -105,7 +105,7 @@ export function generateKeyDerivation(txPubKey: Buffer, secKeyView: Buffer): Buf
/*
* derive_public_key
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L207
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L207
*/
export function derivePublicKey(
derivation: Buffer,
@ -126,7 +126,7 @@ export function derivePublicKey(
/*
* derive_secret_key
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L227
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L227
*/
export function deriveSecretKey(derivation: Buffer, outIndex: number, sec: Buffer): Buffer {
const s: BN = decodeScalar(sec, 'Invalid secret key');
@ -139,7 +139,7 @@ export function deriveSecretKey(derivation: Buffer, outIndex: number, sec: Buffe
/*
* derivation_to_scalar
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L190
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L190
*/
export function derivationToScalar(derivation: Buffer, outIndex: number): Buffer {
const data: Buffer = Buffer.concat([
@ -155,7 +155,7 @@ export function fastHash(data: Buffer): Buffer {
}
/*
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-sugar.h#L1386
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto-sugar.h#L1386
*/
export function hs(str32: Buffer, h: Buffer): Buffer {
const elements: Buffer[] = [str32, h];
@ -165,7 +165,7 @@ export function hs(str32: Buffer, h: Buffer): Buffer {
/*
* hash_to_scalar
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L115
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L115
*/
export function hashToScalar(data: Buffer): Buffer {
const hash: Buffer = fastHash(data);
@ -179,7 +179,7 @@ export function reduceScalar32(scalar: Buffer): Buffer {
/*
* generate_key_image
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L296
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L296
*/
export function calculateKeyImage(pub: Buffer, sec: Buffer): Buffer {
const s: BN = decodeScalar(sec, 'Invalid secret key');
@ -190,7 +190,7 @@ export function calculateKeyImage(pub: Buffer, sec: Buffer): Buffer {
/*
* hash_to_ec
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L286
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L286
*/
export function hashToEc(ephemeralPubKey: Buffer): curve.base.BasePoint {
const hash: Buffer = fastHash(ephemeralPubKey);
@ -200,7 +200,7 @@ export function hashToEc(ephemeralPubKey: Buffer): curve.base.BasePoint {
/*
* ge_fromfe_frombytes_vartime
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto-ops.c#L2209
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto-ops.c#L2209
*/
export function hashToPoint(hash: Buffer): curve.edwards.EdwardsPoint {
const u: RedBN = decodeInt(hash).toRed(ec.curve.red);
@ -292,7 +292,7 @@ export function chachaCrypt(paymentId: Buffer, derivation: Buffer): Buffer {
/*
* keys_from_default
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L88
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L88
*/
export function keysFromDefault(aPart: Buffer, keysSeedBinarySize: number): SpendKeypair {
// aPart == 32 bytes
@ -330,7 +330,7 @@ export function keysFromDefault(aPart: Buffer, keysSeedBinarySize: number): Spen
/*
* generate_seed_keys
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L108
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L108
*/
export function generateSeedKeys(keysSeedBinarySize: number): SpendKeypair {
const keysSeedBinary: Buffer = getRandomBytes(keysSeedBinarySize);
@ -370,7 +370,7 @@ export function getRandomBytes(numBytes: number): Buffer {
/*
* dependent_key
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L129
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L129
*/
export function dependentKey(secretSpendKey: Buffer): string {
if (secretSpendKey.length !== 32) {
@ -382,7 +382,7 @@ export function dependentKey(secretSpendKey: Buffer): string {
/*
* secret_key_to_public_key
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L165
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L165
*/
export function secretKeyToPublicKey(secretViewKey: Buffer): string {
const s: BN = decodeScalar(secretViewKey, 'Invalid secret key');
@ -393,7 +393,7 @@ export function secretKeyToPublicKey(secretViewKey: Buffer): string {
/*
* generate_signature
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L241
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L241
*/
export function generateSignature(message: Buffer, privateKey: Buffer, pubKey: Buffer): string {
const h: Buffer = fastHash(message);
@ -441,7 +441,7 @@ export function generateSignature(message: Buffer, privateKey: Buffer, pubKey: B
/*
* check_signature
* https://github.com/hyle-team/zano/blob/2817090c8ac7639d6f697d00fc8bcba2b3681d90/src/crypto/crypto.cpp#L265
* Upstream ref: hyle-team/zano@2817090src/crypto/crypto.cpp#L265
*/
export function checkSignature(
message: Buffer,

View file

@ -1,24 +1,55 @@
export const TAG_LENGTH = 1;
export const FLAG_LENGTH = 1;
export const SPEND_KEY_LENGTH = 32;
export const VIEW_KEY_LENGTH = 32;
export const CHECKSUM_LENGTH = 4;
// Lethean address prefix as a varint integer.
// Standard addresses start with 'iTHN', integrated with 'iTHn'.
export const ADDRESS_PREFIX = 0x1eaf7;
export const INTEGRATED_ADDRESS_PREFIX = 0xdeaf7;
// Varint-encode a prefix integer into a Buffer.
export function encodeVarint(value: number): Buffer {
const bytes: number[] = [];
while (value >= 0x80) {
bytes.push((value & 0x7f) | 0x80);
value >>>= 7;
}
bytes.push(value);
return Buffer.from(bytes);
}
// Decode a varint from the start of a buffer, returning the value and byte length consumed.
export function decodeVarint(buf: Buffer): { value: number; length: number } {
let value = 0;
let shift = 0;
let length = 0;
let byte: number;
do {
byte = buf[length];
value |= (byte & 0x7f) << shift;
shift += 7;
length++;
} while ((byte & 0x80) !== 0);
return { value, length };
}
export const ADDRESS_PREFIX_LENGTH: number = encodeVarint(ADDRESS_PREFIX).length;
export const INTEGRATED_PREFIX_LENGTH: number = encodeVarint(INTEGRATED_ADDRESS_PREFIX).length;
export const BUFFER_ADDRESS_LENGTH: number =
TAG_LENGTH +
FLAG_LENGTH +
ADDRESS_PREFIX_LENGTH +
SPEND_KEY_LENGTH +
VIEW_KEY_LENGTH +
CHECKSUM_LENGTH;
export const ADDRESS_REGEX = /^Z[a-zA-Z0-9]{96}$/;
export const INTEGRATED_ADDRESS_REGEX = /^iZ[a-zA-Z0-9]{106}$/;
export const ADDRESS_REGEX = /^iTHN[a-zA-Z0-9]{94}$/;
export const INTEGRATED_ADDRESS_REGEX = /^iTHn[a-zA-Z0-9]{105}$/;
export const PAYMENT_ID_LENGTH = 8;
export const INTEGRATED_ADDRESS_FLAG_PREFIX = 0x6c;
export const INTEGRATED_ADDRESS_TAG_PREFIX = 0xf8;
export const ADDRESS_FLAG_PREFIX = 0x01;
export const ADDRESS_TAG_PREFIX = 0xC5;
export const BUFFER_INTEGRATED_ADDRESS_LENGTH =
BUFFER_ADDRESS_LENGTH +
PAYMENT_ID_LENGTH;
export const BUFFER_INTEGRATED_ADDRESS_LENGTH: number =
INTEGRATED_PREFIX_LENGTH +
SPEND_KEY_LENGTH +
VIEW_KEY_LENGTH +
PAYMENT_ID_LENGTH +
CHECKSUM_LENGTH;
export const PAYMENT_ID_REGEX = /^[a-fA-F0-9]{16}$/;
export const PUBLIC_KEY_REGEX = /^[0-9a-f]{64}$/i;
export const HEX_PUBKEY_REGEX = /^[a-fA-F0-9]{64}$/;

View file

@ -3,19 +3,18 @@ import {
BUFFER_ADDRESS_LENGTH,
BUFFER_INTEGRATED_ADDRESS_LENGTH,
CHECKSUM_LENGTH,
FLAG_LENGTH,
INTEGRATED_ADDRESS_REGEX,
PUBLIC_KEY_REGEX,
SPEND_KEY_LENGTH,
TAG_LENGTH,
VIEW_KEY_LENGTH,
decodeVarint,
} from './constants';
import {
DecodeVoutResult,
DecodeTransactionResult,
ZarcanumAddressKeys,
} from './types';
import { isTransactionObjectV3, satoshiToZano } from './utils/functions';
import { isTransactionObjectV3, satoshiToLethean } from './utils/functions';
import { base58Decode } from '../core/base58';
import { getChecksum, secretKeyToPublicKey } from '../core/crypto';
import {
@ -109,7 +108,7 @@ export function decodeTransaction(
return {
ok: true,
amount: satoshiToZano(totalAmount.toString()),
amount: satoshiToLethean(totalAmount.toString()),
...(paymentId ? { paymentId } : { }),
};
}
@ -187,15 +186,17 @@ function getKeysFromAddress(address: string): ZarcanumAddressKeys {
throw new Error('Invalid address checksum');
}
const { length: prefixLength } = decodeVarint(buf);
const spendPublicKey: string = Buffer.from(
buf.buffer,
TAG_LENGTH + FLAG_LENGTH,
prefixLength,
SPEND_KEY_LENGTH,
).toString('hex');
const viewPublicKey: string = Buffer.from(
buf.buffer,
TAG_LENGTH + FLAG_LENGTH + SPEND_KEY_LENGTH,
prefixLength + SPEND_KEY_LENGTH,
VIEW_KEY_LENGTH,
).toString('hex');

View file

@ -7,7 +7,7 @@ export function timestampMsToDate(timestampMs: number): Date {
return new Date(timestampMs * 1000);
}
export function satoshiToZano(satoshiAmount: string): string {
export function satoshiToLethean(satoshiAmount: string): string {
const satoshi: Big = new Big(satoshiAmount);
if (satoshi.lt(0)) {
throw new Error('The number of satoshi cannot be negative');

View file

@ -1,6 +1,8 @@
export const CRYPTO_HDS_OUT_AMOUNT_MASK = Buffer.from('ZANO_HDS_OUT_AMOUNT_MASK_______\0', 'ascii');
export const CRYPTO_HDS_OUT_CONCEALING_POINT = Buffer.from('ZANO_HDS_OUT_CONCEALING_POINT__\0', 'ascii');
export const CRYPTO_HDS_OUT_ASSET_BLIND_MASK = Buffer.from('ZANO_HDS_OUT_ASSET_BLIND_MASK__\0', 'ascii');
// Note: The ZANO_HDS_* string values above are protocol-level constants shared with the Lethean
// blockchain daemon. They must not be changed or transaction decoding will break.
export const POINT_X: Buffer = Buffer.from('3a25bcdb43f5d2c9dd063dc39a9e0987bafc6fcf2df1bc76322d75884a4a3820', 'hex');
export const NATIVE_ASSET_ID: Buffer = Buffer.from(
'd6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a',

View file

@ -24,7 +24,7 @@ describe('generate account', () => {
});
expect(generateAccount()).toMatchObject({
address: 'ZxBpGz8qdG3SxgBYFKJqxjThPZpjqW1ouK3ZsyZnZCUqQ4Ndc9PiqkdJuEicMXPPSW5JidxK5bye7UYc1hkTHhxc1w4temC2A',
address: 'iTHNJKCusvaD9NZX9bjzTnVCrp92549Gf7m926mybX5KVXSMHP5Ejk486WUfM5FtiTgzaLmyGXcvrdEiFWyPKihW3BDTVzaifp',
secretSpendKey: '6c225665aadb81ebce41bd94cbc78250aaf62f2636819b1cdcf47d4cbcd2b00d',
publicSpendKey: '0c27ece0fb489b344915d12745a89f9b6cb307c384286be12ae9311942aa89db',
});
@ -32,7 +32,7 @@ describe('generate account', () => {
});
describe('address validation', () => {
const address = 'ZxDFpn4k7xVYyc9VZ3LphrJbkpc46xfREace5bme1aXiMzKPAHA8jsTWcHSXhv9AdodSaoGXK9Mg7bk3ec4FkQrj357fZPWZX';
const address = 'iTHNdF5eT7c9VJpNtt3geei3o4mye12eFe6bNZAzxtiBg49XYns3HAkBVBn3N46s9A8Ai7PBbZxnbQZ3huf2zg3N3AA2NQ5bHR';
const secretSpendKey = '80b3e96a3eb765332b0fd3e44e0fefa58747a70025bf91aa4a7b758ab6f5590d';
const publicSpendKey = 'b3eee2376f32bf2bfb5cf9c023f569380c84ac8c64ddc8f7c109730dc8e97d7a';
const secretViewKey = '3e75ffee51eb21b1d6404ddcab5b3aaa49edbfe225e9a893d87074aacae46b09';

View file

@ -1,54 +1,48 @@
import { splitIntegratedAddress, getIntegratedAddress, getKeysFromAddress, encodeAddress } from '../src/address/address-utils';
import { ADDRESS_PREFIX } from '../src/address/constants';
import { base58Decode } from '../src/core/base58';
describe(
'testing the correctness of the address encoding function encodeAddress',
() => {
const tag = 197;
const flag = 1;
const prefix = ADDRESS_PREFIX;
const spendPublicKey = '9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa';
const viewPublicKey = 'a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3';
const address = encodeAddress(tag, flag, spendPublicKey, viewPublicKey);
const address = encodeAddress(prefix, spendPublicKey, viewPublicKey);
it('checking the correctness of the result', () => {
expect(address)
.toBe('ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH');
.toBe('iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi');
});
it('checking the correctness of the address length', () => {
expect(address).toHaveLength(97);
expect(address).toHaveLength(98);
});
it('should throw an error for invalid tag', () => {
it('should throw an error for invalid prefix', () => {
expect(() => {
encodeAddress(-197, 1, '...', '...');
}).toThrow('Invalid tag');
});
it('should throw an error for invalid flag', () => {
expect(() => {
encodeAddress(197, -1, '...', '...');
}).toThrow('Invalid flag');
encodeAddress(-1, '...', '...');
}).toThrow('Invalid prefix');
});
it('should throw an error for invalid public key', () => {
expect(() => {
encodeAddress(197, 1, 'invalid', viewPublicKey);
encodeAddress(prefix, 'invalid', viewPublicKey);
}).toThrow('Invalid spendPublicKey: must be a hexadecimal string with a length of 64');
});
it('should throw an error for invalid private key', () => {
expect(() => {
encodeAddress(197, 1, spendPublicKey, 'invalid');
encodeAddress(prefix, spendPublicKey, 'invalid');
}).toThrow('Invalid viewPrivateKey: must be a hexadecimal string with a length of 64');
});
},
);
describe(
'testing the correctness of the address decoding function getKeysFromZanoAddress',
'testing the correctness of the address decoding function getKeysFromLetheanAddress',
() => {
const address = 'ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH';
const address = 'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi';
const spendPublicKey = '9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa';
const viewPublicKey = 'a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3';
@ -66,14 +60,14 @@ describe(
});
it('should throw an invalid character in base58 string', () => {
const invalidAddress = 'ZxD5aoLDPTdcaRx4uOpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH';
const invalidAddress = 'iTHNavWQHS59AinRwcPqSqBKjckre7PgOZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi';
expect(() => {
getKeysFromAddress(invalidAddress);
}).toThrow('base58 string block contains invalid character');
});
it('should throw an invalid base58 string size', () => {
const invalidAddress = 'Z';
const invalidAddress = 'iTHN';
expect(() => {
base58Decode(invalidAddress);
}).toThrow('base58 string has an invalid size');
@ -81,51 +75,50 @@ describe(
it('should throw an invalid address checksum', () => {
expect(() => {
(getKeysFromAddress('Zx' + '1'.repeat(95)));
(getKeysFromAddress('iTHN' + '1'.repeat(94)));
}).toThrow('Invalid address checksum');
});
},
);
describe('getIntegratedAddress', () => {
const SUFFIX_LENGTH = 18; // paymentId + checksum
// The last SUFFIX_LENGTH characters of an integrated address encode the paymentId + checksum,
// which differ each time a random paymentId is generated. The deterministic prefix is stable.
const SUFFIX_LENGTH = 17;
// Define test data
const integratedAddress = 'iZ2kFmwxRHoaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e47JPoHxGL5UU2D1tpQMg4';
const masterAddress = 'ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH';
const masterAddress2 = 'ZxDG8UrQMEVaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e41KkqnWH8F';
// Define test data - Lethean iTHN/iTHn addresses
const integratedAddress = 'iTHnVGr7tms9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJQSA6PNs1faBfevEK7';
const masterAddress = 'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi';
const masterAddress2 = 'iTHNV7oXHXXUtKxTWVi267XoQMNrbVzFk2tz6tmus8doM5KuSrewKQ2bMbwmzcBEhJGuGNwfPMnuxc6Rx5ZvvXro86z84X5t3V';
const masterBasedIntegratedAddress = 'iZ2Zi6RmTWwcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp3iTqEsjvJoco1aLSZXS6T';
const master2BasedIntegratedAddress = 'iZ2kFmwxRHoaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e47JQQbd6iYGx1S6AdHpq6';
// Compute desired outcomes for the slice operation
// The deterministic prefix of an integrated address derived from each input
const integratedAddressWithoutSuffix: string = integratedAddress.slice(0, -SUFFIX_LENGTH);
const masterBasedIntegratedAddressWithoutSuffix: string = masterBasedIntegratedAddress.slice(0, -SUFFIX_LENGTH);
const master2BasedIntegratedAddressWithoutSuffix: string = master2BasedIntegratedAddress.slice(0, -SUFFIX_LENGTH);
const masterBasedPrefix: string = 'iTHnVGr7tms9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJ';
const master2BasedPrefix: string = 'iTHnPU9EtsKUtKxTWVi267XoQMNrbVzFk2tz6tmus8doM5KuSrewKQ2bMbwmzcBEhJGuGNwfPMnuxc6Rx5ZvvXroYMR';
// Addresses returned by ZanoAddressUtils
// Addresses returned by getIntegratedAddress (random paymentId each time)
const addressFromIntegrated: string = getIntegratedAddress(integratedAddress);
const addressFromMaster: string = getIntegratedAddress(masterAddress);
const addressFromMaster2: string = getIntegratedAddress(masterAddress2);
it('ensures that truncating the last 18 characters from the integrated address is correct', () => {
it('ensures deterministic prefix from integrated address input is correct', () => {
expect(addressFromIntegrated.slice(0, -SUFFIX_LENGTH)).toBe(integratedAddressWithoutSuffix);
});
it('ensures that truncating the last 18 characters from the master-based integrated address is correct', () => {
expect(addressFromMaster.slice(0, -SUFFIX_LENGTH)).toBe(masterBasedIntegratedAddressWithoutSuffix);
it('ensures deterministic prefix from master address input is correct', () => {
expect(addressFromMaster.slice(0, masterBasedPrefix.length)).toBe(masterBasedPrefix);
});
it('ensures that truncating the last 18 characters from the second master-based integrated address is correct', () => {
expect(addressFromMaster2.slice(0, -SUFFIX_LENGTH)).toBe(master2BasedIntegratedAddressWithoutSuffix);
it('ensures deterministic prefix from second master address input is correct', () => {
expect(addressFromMaster2.slice(0, master2BasedPrefix.length)).toBe(master2BasedPrefix);
});
});
describe(
'testing the correctness of the address decoding in function splitIntegratedAddress',
() => {
const integratedAddress = 'iZ2kFmwxRHoaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e47JPoHxGL5UU2D1tpQMg4';
const masterAddress = 'ZxDG8UrQMEVaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH99BJ6cnbAtJTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e41KkqnWH8F';
const integratedAddress = 'iTHnVGr7tms9AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jLYcEJQSA6PNs1faBfevEK7';
const masterAddress = 'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi';
const paymentId = '1e4cbed444118c99';
const invalidintegratedAddress = 'i03McELC3jGTgUnZjPj9CTCTKy1qqM9wPCTp92uBC7e47JR67Qv6wMFaRxm1ni8HnfUTkYuKbni8s4CE2Z4GgFfH999Pvhkaga42D1npn1Vc';

View file

@ -1,9 +1,11 @@
import { encodeAddress, getKeysFromAddress } from '../src/address/address-utils';
import { ADDRESS_PREFIX } from '../src/address/constants';
import { base58Decode, base58Encode } from '../src/core/base58';
import type { ZarcanumAddressKeys } from '../src/address/types';
const rawAddressBufferHex = 'c5 01 9f 5e 1f a9 36 30 d4 b2 81 b1 8b b6 7a 3d b7 9e 96 22 fc 70 3c c3 ad 4a 45 3a 82 e0 a3 6d 51 fa a3 f2 08 c8 f9 ba 49 ba b2 8e ed 62 b3 5b 0f 6b e0 a2 97 bc d8 5c 2f aa 1e b1 82 05 27 bc f7 e3 e2 38 1c d6';
// Lethean varint prefix 0x1eaf7 = [0xf7, 0xd5, 0x07] followed by spend key, view key, checksum
const rawAddressBufferHex = 'f7 d5 07 9f 5e 1f a9 36 30 d4 b2 81 b1 8b b6 7a 3d b7 9e 96 22 fc 70 3c c3 ad 4a 45 3a 82 e0 a3 6d 51 fa a3 f2 08 c8 f9 ba 49 ba b2 8e ed 62 b3 5b 0f 6b e0 a2 97 bc d8 5c 2f aa 1e b1 82 05 27 bc f7 e3 ac c8 50 23';
function dataToEncodeFn(hexString: string): string {
return hexString.split(' ')
@ -60,7 +62,7 @@ function runTests() {
testEncode('1111111111111111111111', '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00');
testEncode('22222222222VtB5VXc', '\\x06\\x15\\x60\\x13\\x76\\x28\\x79\\xF7\\xFF\\xFF\\xFF\\xFF\\xFF');
const expectedAddress = 'ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH';
const expectedAddress = 'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi';
const dataToAddress: string = dataToEncodeFn(rawAddressBufferHex);
testEncode(expectedAddress, dataToAddress);
}
@ -68,7 +70,7 @@ function runTests() {
function runTestEncodeAddress(address: string, viewPubKey: string, spendPubKey: string): void {
const addressBufferHex: string = dataToEncodeFn(bufferToHex(base58Decode(address)));
testEncode(
'ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH',
'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi',
addressBufferHex,
);
@ -84,14 +86,14 @@ function runTestEncodeAddress(address: string, viewPubKey: string, spendPubKey:
throw new Error('PubSpendKey not matched.');
}
const encodedAddress: string = encodeAddress(197, 1, spendPubKey, viewPubKey);
const encodedAddress: string = encodeAddress(ADDRESS_PREFIX, spendPubKey, viewPubKey);
if(encodedAddress !== address) {
throw new Error(`Encoded address not matched. Received ${encodedAddress}, Expected: ${address}`);
}
}
function runTestGetZanoKeys(address: string, viewPubKey: string, spendPubKey: string): void {
function runTestGetLetheanKeys(address: string, viewPubKey: string, spendPubKey: string): void {
const keysFromAddress: ZarcanumAddressKeys = getKeysFromAddress(address);
if(keysFromAddress.spendPublicKey !== spendPubKey) {
@ -106,12 +108,12 @@ function runTestGetZanoKeys(address: string, viewPubKey: string, spendPubKey: st
void (async (): Promise<void> => {
runTests();
runTestEncodeAddress(
'ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH',
'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi',
'a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3',
'9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa',
);
runTestGetZanoKeys(
'ZxD5aoLDPTdcaRx4uCpyW4XiLfEXejepAVz8cSY2fwHNEiJNu6NmpBBDLGTJzCsUvn3acCVDVDPMV8yQXdPooAp338Se7AxeH',
runTestGetLetheanKeys(
'iTHNavWQHS59AinRwcPqSqBKjckre7PgoZjK3q5YG2fUKHYWFZMWjB6YAEAdw4yDDUGEQ7CGEgbqhGRKeadGV1jL8ALnEwGMWi',
'a3f208c8f9ba49bab28eed62b35b0f6be0a297bcd85c2faa1eb1820527bcf7e3',
'9f5e1fa93630d4b281b18bb67a3db79e9622fc703cc3ad4a453a82e0a36d51fa',
);

View file

@ -10,8 +10,8 @@ describe(
const secretViewKey = '81ef6415b815f675991585ebba71c8c4663a08893fd93ee149c48e797a2fdf09';
const publicSpendKey = '6be99667faa6b693fbcd808f94b8243540198c9cbb0b564f267f885d227804a2';
const integratedAddress = 'iZ285wzfsbjXYEgZVuDeFy74GFtYDFbvrFSJpL6TJQ3Z1mxZaKQLZsJHyVuCH4pQSr3rQunoH7cE4bjJSWmJqWSnKu6rPqXZ8HB1VxSHXVZR';
const address = 'ZxCdxeu7oYRXYEgZVuDeFy74GFtYDFbvrFSJpL6TJQ3Z1mxZaKQLZsJHyVuCH4pQSr3rQunoH7cE4bjJSWmJqWSn1yGLxsfJH';
const integratedAddress = 'iTHnPU9EtsKUtKxTWVi267XoQMNrbVzFk2tz6tmus8doM5KuSrewKQ2bMbwmzcBEhJGuGNwfPMnuxc6Rx5ZvvXroYMR846UJ1H4662LyVK6oo';
const address = 'iTHNV7oXHXXUtKxTWVi267XoQMNrbVzFk2tz6tmus8doM5KuSrewKQ2bMbwmzcBEhJGuGNwfPMnuxc6Rx5ZvvXro86z84X5t3V';
it('checking the correctness of the result', () => {
const result: DecodeTransactionResult = decodeTransaction(objectInJson, secretViewKey, publicSpendKey);

1543
yarn.lock

File diff suppressed because it is too large Load diff