mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-27 15:45:48 +00:00
137 lines
4.1 KiB
TypeScript
137 lines
4.1 KiB
TypeScript
import {
|
|
codec,
|
|
encodeSeed,
|
|
decodeSeed,
|
|
encodeAccountID,
|
|
decodeAccountID,
|
|
encodeNodePublic,
|
|
decodeNodePublic,
|
|
encodeAccountPublic,
|
|
decodeAccountPublic,
|
|
isValidClassicAddress
|
|
} from './xrp-codec'
|
|
import * as assert from 'assert'
|
|
|
|
const PREFIX_BYTES = {
|
|
MAIN: Buffer.from([0x05, 0x44]), // 5, 68
|
|
TEST: Buffer.from([0x04, 0x93]) // 4, 147
|
|
}
|
|
|
|
function classicAddressToXAddress(classicAddress: string, tag: number | false, test: boolean): string {
|
|
const accountId = decodeAccountID(classicAddress)
|
|
return encodeXAddress(accountId, tag, test)
|
|
}
|
|
|
|
function encodeXAddress(accountId: Buffer, tag: number | false, test: boolean): string {
|
|
if (accountId.length !== 20) {
|
|
// RIPEMD160 is 160 bits = 20 bytes
|
|
throw new Error('Account ID must be 20 bytes')
|
|
}
|
|
const MAX_32_BIT_UNSIGNED_INT = 4294967295
|
|
const flag = tag === false ? 0 : tag <= MAX_32_BIT_UNSIGNED_INT ? 1 : 2
|
|
if (flag === 2) {
|
|
throw new Error('Invalid tag')
|
|
}
|
|
if (tag === false) {
|
|
tag = 0
|
|
}
|
|
const bytes = Buffer.concat(
|
|
[
|
|
test ? PREFIX_BYTES.TEST : PREFIX_BYTES.MAIN,
|
|
accountId,
|
|
Buffer.from(
|
|
[
|
|
flag, // 0x00 if no tag, 0x01 if 32-bit tag
|
|
tag & 0xff, // first byte
|
|
(tag >> 8) & 0xff, // second byte
|
|
(tag >> 16) & 0xff, // third byte
|
|
(tag >> 24) & 0xff, // fourth byte
|
|
0, 0, 0, 0 // four zero bytes (reserved for 64-bit tags)
|
|
]
|
|
)
|
|
]
|
|
)
|
|
const xAddress = codec.encodeChecked(bytes)
|
|
return xAddress
|
|
}
|
|
|
|
function xAddressToClassicAddress(xAddress: string): {classicAddress: string, tag: number | false, test: boolean} {
|
|
const {
|
|
accountId,
|
|
tag,
|
|
test
|
|
} = decodeXAddress(xAddress)
|
|
const classicAddress = encodeAccountID(accountId)
|
|
return {
|
|
classicAddress,
|
|
tag,
|
|
test
|
|
}
|
|
}
|
|
|
|
function decodeXAddress(xAddress: string): {accountId: Buffer, tag: number | false, test: boolean} {
|
|
const decoded = codec.decodeChecked(xAddress)
|
|
const test = isBufferForTestAddress(decoded)
|
|
const accountId = decoded.slice(2, 22)
|
|
const tag = tagFromBuffer(decoded)
|
|
return {
|
|
accountId,
|
|
tag,
|
|
test
|
|
}
|
|
}
|
|
|
|
function isBufferForTestAddress(buf: Buffer): boolean {
|
|
const decodedPrefix = buf.slice(0, 2)
|
|
if (PREFIX_BYTES.MAIN.equals(decodedPrefix)) {
|
|
return false
|
|
} else if (PREFIX_BYTES.TEST.equals(decodedPrefix)) {
|
|
return true
|
|
} else {
|
|
throw new Error('Invalid X-address: bad prefix')
|
|
}
|
|
}
|
|
|
|
function tagFromBuffer(buf: Buffer): number | false {
|
|
const flag = buf[22]
|
|
if (flag >= 2) {
|
|
// No support for 64-bit tags at this time
|
|
throw new Error('Unsupported X-address')
|
|
}
|
|
if (flag === 1) {
|
|
// Little-endian to big-endian
|
|
return buf[23] + buf[24] * 0x100 + buf[25] * 0x10000 + buf[26] * 0x1000000
|
|
}
|
|
assert.strictEqual(flag, 0, 'flag must be zero to indicate no tag')
|
|
assert.ok(Buffer.from('0000000000000000', 'hex').equals(buf.slice(23, 23 + 8)),
|
|
'remaining bytes must be zero')
|
|
return false
|
|
}
|
|
|
|
function isValidXAddress(xAddress: string): boolean {
|
|
try {
|
|
decodeXAddress(xAddress)
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
export {
|
|
codec, // Codec with XRP alphabet
|
|
encodeSeed, // Encode entropy as a "seed"
|
|
decodeSeed, // Decode a seed into an object with its version, type, and bytes
|
|
encodeAccountID, // Encode bytes as a classic address (r...)
|
|
decodeAccountID, // Decode a classic address to its raw bytes
|
|
encodeNodePublic, // Encode bytes to XRP Ledger node public key format
|
|
decodeNodePublic, // Decode an XRP Ledger node public key into its raw bytes
|
|
encodeAccountPublic, // Encode a public key, as for payment channels
|
|
decodeAccountPublic, // Decode a public key, as for payment channels
|
|
isValidClassicAddress, // Check whether a classic address (r...) is valid
|
|
classicAddressToXAddress, // Derive X-address from classic address, tag, and network ID
|
|
encodeXAddress, // Encode account ID, tag, and network ID to X-address
|
|
xAddressToClassicAddress, // Decode X-address to account ID, tag, and network ID
|
|
decodeXAddress, // Convert X-address to classic address, tag, and network ID
|
|
isValidXAddress // Check whether an X-address (X...) is valid
|
|
}
|