UNPKG

@massalabs/massa-web3

Version:
219 lines 7.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Address = exports.AddressType = void 0; const tslib_1 = require("tslib"); const base58_1 = tslib_1.__importDefault(require("../crypto/base58")); const versioner_1 = require("../crypto/interfaces/versioner"); const varintVersioner_1 = tslib_1.__importDefault(require("../crypto/varintVersioner")); const varint_1 = tslib_1.__importDefault(require("varint")); const ADDRESS_USER_PREFIX = 'AU'; const ADDRESS_CONTRACT_PREFIX = 'AS'; const UNDERLYING_HASH_LEN = 32; const DEFAULT_VERSION = versioner_1.Version.V0; var AddressType; (function (AddressType) { AddressType[AddressType["EOA"] = 0] = "EOA"; AddressType[AddressType["Contract"] = 1] = "Contract"; })(AddressType || (exports.AddressType = AddressType = {})); /** * Get the address version. * * @returns the address version. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars function getVersion(data) { // when a new version will come, implement the logic to detect version here // This should be done without serializer and versionner as they are potentially not known at this point return versioner_1.Version.V0; } /** * Get the address prefix. * * @remarks * Throws an error if the prefix is not valid. * * @returns the address version. */ function getPrefix(str) { const expected = [ADDRESS_USER_PREFIX, ADDRESS_CONTRACT_PREFIX]; for (const prefix of expected) { if (str.startsWith(prefix)) { return prefix; } } throw new Error(`invalid address prefix: one of ${expected.join(' or ')} was expected.`); } /** * A class representing an address. * * @remarks * For now, an address is intrinsically linked (to be understood as a digest of) to the public key. * * @privateRemarks * Interfaces are used to make the code more modular. To add a new version, you simply need to: * - Add a new case in the switch statement with the new algorithms to use. * - Change the DEFAULT_VERSION version matching the last version. * - Change the getVersion method to detect the version from user input. * - check the `fromPublicKey` method to potentially adapt how an address is derived from a public key. * - Voila! The code will automatically handle the new version. */ class Address { serializer; versioner; version; // The address in byte format. Address type and version included. bytes; isEOA = false; constructor(serializer, versioner, version) { this.serializer = serializer; this.versioner = versioner; this.version = version; } /** * Initialize a new address object from a version. * * @param version - The version of the address. * * @returns A new address instance. * * @throws If the version is not supported. */ static initFromVersion(version = DEFAULT_VERSION) { switch (version) { case versioner_1.Version.V0: return new Address(new base58_1.default(), new varintVersioner_1.default(), version); default: throw new Error(`unsupported version: ${version}`); } } /** * Initializes a new address object from a serialized string. * * @param str - The serialized address string. * * @returns A new address instance. * * @throws If the address string is invalid. */ static fromString(str) { try { const version = getVersion(str); const address = Address.initFromVersion(version); const prefix = getPrefix(str); address.isEOA = prefix === ADDRESS_USER_PREFIX; const versionedBytes = address.serializer.deserialize(str.slice(prefix.length)); const { version: extractedVersion } = address.versioner.extract(versionedBytes); // safety check if (extractedVersion !== version) { throw new Error(`invalid version: ${version}. ${address.version} was expected.`); } address.bytes = Uint8Array.from([ ...varint_1.default.encode(address.isEOA ? AddressType.EOA : AddressType.Contract), ...versionedBytes, ]); return address; } catch (e) { throw new Error(`invalid address string: ${e.message}`); } } /** * Get the address type from bytes. * * @returns the address type enum. */ getType() { if (!this.bytes) { throw new Error('address bytes is not initialized'); } return varint_1.default.decode(this.bytes); } /** * Initializes a new address object from a public key. * * @param publicKey - The public key to derive the address from. * * @returns A new address object. */ static fromPublicKey(publicKey) { if (publicKey.version !== versioner_1.Version.V0) { throw new Error(`unsupported public key version: ${publicKey.version}`); } const address = Address.initFromVersion(versioner_1.Version.V0); const rawBytes = publicKey.hasher.hash(publicKey.toBytes()); address.bytes = Uint8Array.from([ AddressType.EOA, ...address.versioner.attach(versioner_1.Version.V0, rawBytes), ]); address.isEOA = true; return address; } /** * Initializes a new address object from versioned bytes. * * @param bytes - The versioned bytes. * * @returns A new address object. */ static fromBytes(bytes) { const version = getVersion(bytes); const address = Address.initFromVersion(version); address.bytes = bytes; // safety check const addressType = address.getType(); const versionedBytes = bytes.slice(varint_1.default.encodingLength(addressType)); const { version: extractedVersion } = address.versioner.extract(versionedBytes); if (extractedVersion !== version) { throw new Error(`invalid version: ${version}. ${address.version} was expected.`); } address.isEOA = addressType === AddressType.EOA; return address; } /** * Versions the address key bytes. * * @returns The versioned address key bytes. */ toBytes() { return this.bytes; } /** * Serializes the address to a string. * * @remarks * A address is serialized as follows: * - The version is serialized as a varint and prepended to the address bytes. * - The result is then sent to the serializer. * * @returns The serialized address string. */ toString() { // skip address type bytes const versionedBytes = this.bytes.slice(varint_1.default.encodingLength(this.getType())); return `${this.isEOA ? ADDRESS_USER_PREFIX : ADDRESS_CONTRACT_PREFIX}${this.serializer.serialize(versionedBytes)}`; } /** * Get address in binary format from a bytes buffer. * * @returns The address in bytes format. */ static extractFromBuffer(data, offset = 0) { // addr type varint_1.default.decode(data, offset); const typeLen = varint_1.default.decode.bytes; if (typeLen === undefined) { throw new Error('invalid address: type not found.'); } // version varint_1.default.decode(data, offset + typeLen); const versionLen = varint_1.default.decode.bytes; if (versionLen === undefined) { throw new Error('invalid address: version not found.'); } const addrLen = typeLen + versionLen + UNDERLYING_HASH_LEN; const extractedData = data.slice(offset, offset + addrLen); return { data: extractedData, length: addrLen }; } } exports.Address = Address; //# sourceMappingURL=address.js.map