From 5eb9311a0afd7354e2e8541f0611d0735de695c9 Mon Sep 17 00:00:00 2001 From: Oleksandr Myshchyshyn Date: Tue, 28 Jan 2025 19:43:36 +0200 Subject: [PATCH 1/2] Extend BidAddr RC5 --- src/types/key/BidAddr.test.ts | 92 ++++++ src/types/key/BidAddr.ts | 595 +++++++++++++++++++++------------- 2 files changed, 453 insertions(+), 234 deletions(-) create mode 100644 src/types/key/BidAddr.test.ts diff --git a/src/types/key/BidAddr.test.ts b/src/types/key/BidAddr.test.ts new file mode 100644 index 000000000..437d6e806 --- /dev/null +++ b/src/types/key/BidAddr.test.ts @@ -0,0 +1,92 @@ +import { expect } from 'chai'; + +import { Key } from './Key'; +import { BidAddrTag } from './BidAddr'; + +describe('BidAddr', () => { + it('should correctly create a new key for validator BidAddr', () => { + const validatorBidAddrKeyStr = + 'bid-addr-012f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309'; + const key = Key.newKey(validatorBidAddrKeyStr); + + expect(key.toPrefixedString()).to.equal(validatorBidAddrKeyStr); + expect(key.bidAddr?.getTag()).to.equal(BidAddrTag.ValidatorTag); + expect(key.bidAddr?.validator).not.to.be.undefined; + }); + + it('should correctly create a new key for unified BidAddr', () => { + const unifiedBidAddrKeyStr = + 'bid-addr-002f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309'; + const key = Key.newKey(unifiedBidAddrKeyStr); + + expect(key.toPrefixedString()).to.equal(unifiedBidAddrKeyStr); + expect(key.bidAddr?.getTag()).to.equal(BidAddrTag.UnifiedTag); + expect(key.bidAddr?.unified).not.to.be.undefined; + }); + + it('should correctly create a new key for delegator BidAddr', () => { + const delegatorBidAddrKeyStr = + 'bid-addr-022f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c4580073099fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4'; + const key = Key.newKey(delegatorBidAddrKeyStr); + + expect(key.toPrefixedString()).to.equal(delegatorBidAddrKeyStr); + expect(key.bidAddr?.getTag()).to.equal(BidAddrTag.DelegatedAccountTag); + expect(key.bidAddr?.delegatorAccount).not.to.be.undefined; + expect(key.bidAddr?.validator).not.to.be.undefined; + expect(key.bidAddr?.delegatorAccount?.toHex()).to.deep.equal( + '9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4' + ); + expect(key.bidAddr?.validator?.toHex()).to.deep.equal( + '2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309' + ); + }); + + it('should correctly create a new key for credit BidAddr', () => { + const creditBidAddrKeyStr = + 'bid-addr-04520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d097f00000000000000'; + const key = Key.newKey(creditBidAddrKeyStr); + + expect(key.toPrefixedString()).to.equal(creditBidAddrKeyStr); + expect(key.bidAddr?.getTag()).to.equal(BidAddrTag.CreditTag); + expect(key.bidAddr?.validator).not.to.be.undefined; + expect(key.bidAddr?.eraId).not.to.be.undefined; + expect(key.bidAddr?.eraId).to.deep.equal(127); + expect(key.bidAddr?.validator?.toHex()).to.deep.equal( + '520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d09' + ); + }); + + it('should correctly create a new key for delegatorPurse BidAddr', () => { + const delegatorPurseKeyStr = + 'bid-addr-03da3cd8cc4c8f34e7731583e67ddc211ff9b5c3f2c52640582415c2cce9315b2a8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c'; + const key = Key.newKey(delegatorPurseKeyStr); + + expect(key.toPrefixedString()).to.equal(delegatorPurseKeyStr); + expect(key.bidAddr?.getTag()).to.equal(BidAddrTag.DelegatedPurseTag); + expect(key.bidAddr?.validator).not.to.be.undefined; + expect(key.bidAddr?.delegatorPurseAddress).not.to.be.undefined; + expect(key.bidAddr?.validator?.toHex()).to.deep.equal( + 'da3cd8cc4c8f34e7731583e67ddc211ff9b5c3f2c52640582415c2cce9315b2a' + ); + expect(key.bidAddr?.delegatorPurseAddress).to.deep.equal( + '8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c' + ); + }); + + it('should correctly create a new key for unbondPurse BidAddr', () => { + const unbondPurseKeyStr = + 'bid-addr-08da3cd8cc4c8f34e7731583e67ddc211ff9b5c3f2c52640582415c2cce9315b2a8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c'; + const key = Key.newKey(unbondPurseKeyStr); + + expect(key.toPrefixedString()).to.equal(unbondPurseKeyStr); + expect(key.bidAddr?.getTag()).to.equal(BidAddrTag.UnbondPurseTag); + expect(key.bidAddr?.validator).not.to.be.undefined; + expect(key.bidAddr?.delegatorPurseAddress).not.to.be.undefined; + expect(key.bidAddr?.validator?.toHex()).to.deep.equal( + 'da3cd8cc4c8f34e7731583e67ddc211ff9b5c3f2c52640582415c2cce9315b2a' + ); + expect(key.bidAddr?.delegatorPurseAddress).to.deep.equal( + '8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c' + ); + }); +}); diff --git a/src/types/key/BidAddr.ts b/src/types/key/BidAddr.ts index 8210bc8cb..5e14de9e2 100644 --- a/src/types/key/BidAddr.ts +++ b/src/types/key/BidAddr.ts @@ -1,185 +1,168 @@ import { BigNumber } from '@ethersproject/bignumber'; -import { jsonObject, jsonMember } from 'typedjson'; +import { jsonMember, jsonObject } from 'typedjson'; import { Hash } from './Hash'; import { IResultWithBytes } from '../clvalue'; +import { HexBytes } from '../HexBytes'; +import { toBytesString } from '../ByteConverters'; /** - * Enum representing the types of bid addresses. - * Each type corresponds to a unique tag value that identifies the specific type of bid address. + * Enum representing the different types of bid addresses. + * Each type corresponds to a specific tag value that uniquely identifies the bid address type. */ export enum BidAddrTag { - Unified = 0, - Validator = 1, - Delegator = 2, - Credit = 4 + UnifiedTag = 0, + ValidatorTag, + DelegatedAccountTag, + DelegatedPurseTag, + CreditTag, + ReservedDelegationAccountTag, + ReservedDelegationPurseTag, + UnbondAccountTag, + UnbondPurseTag } -/** Error thrown when an invalid BidAddrTag is encountered. */ -export const ErrInvalidBidAddrTag = new Error('invalid BidAddrTag'); +/** Error indicating an invalid BidAddrTag was encountered. */ +export const ErrInvalidBidAddrTag = new Error('Invalid BidAddrTag'); -/** Error thrown when an unexpected BidAddrTag is found in a BidAddr. */ +/** Error indicating an unexpected BidAddrTag was found in a BidAddr. */ export const ErrUnexpectedBidAddrTagInBidAddr = new Error( - 'unexpected BidAddrTag in BidAddr' + 'Unexpected BidAddrTag in BidAddr' ); -/** Error thrown when the BidAddr format is invalid. */ -export const ErrInvalidBidAddrFormat = new Error('invalid BidAddr format'); +/** Error indicating the BidAddr format is invalid. */ +export const ErrInvalidBidAddrFormat = new Error('Invalid BidAddr format'); -/** - * Utility class for handling operations related to BidAddrTag. - */ -export class BidAddrTagUtils { - /** - * Converts a byte to a BidAddrTag. - * @param tag - The byte value to convert. - * @returns The corresponding BidAddrTag. - * @throws {ErrInvalidBidAddrTag} If the byte doesn't match a valid BidAddrTag. - */ - static fromByte(tag: number): BidAddrTag { - const addrTag = tag as BidAddrTag; - if ( - addrTag !== BidAddrTag.Unified && - addrTag !== BidAddrTag.Validator && - addrTag !== BidAddrTag.Delegator && - addrTag !== BidAddrTag.Credit - ) { - throw ErrInvalidBidAddrTag; - } - return addrTag; - } -} - -const UnifiedOrValidatorAddrLen = 33; -const CreditAddrLen = 41; +// Constants defining specific key lengths based on tag type +const UnifiedOrValidatorAddrLen = 33; // BidAddrTag(1) + Hash(32) +const CreditAddrLen = 41; // BidAddrTag(1) + Hash(32) + EraId(8) +const ValidatorHashDelegatorAddrLen = 65; // BidAddrTag(1) + Hash(32) + Hash(32) /** - * Represents information for a delegator in a Delegator BidAddr. - */ -@jsonObject -export class DelegatorInfo { - /** The validator hash associated with this delegator. */ - @jsonMember({ - name: 'Validator', - constructor: Hash, - deserializer: json => Hash.fromJSON(json), - serializer: value => value.toJSON() - }) - validator: Hash; - - /** The delegator's hash. */ - @jsonMember({ - name: 'Delegator', - constructor: Hash, - deserializer: json => Hash.fromJSON(json), - serializer: value => value.toJSON() - }) - delegator: Hash; -} - -/** - * Represents credit information within a Credit BidAddr. - */ -@jsonObject -export class CreditInfo { - /** The validator associated with this credit. */ - @jsonMember({ - name: 'Validator', - constructor: Hash, - deserializer: json => Hash.fromJSON(json), - serializer: value => value.toJSON() - }) - validator: Hash; - - /** The era ID for this credit. */ - @jsonMember({ name: 'EraId', constructor: Number }) - eraId: number; -} - -/** - * Represents a bid address, which can store information for unified, validator, delegator, or credit types. + * Represents a bid address, which stores information such as unified, validator, delegator, or credit data types. */ @jsonObject export class BidAddr { - /** The unified hash if this is a unified bid address. */ + /** Unified hash for UnifiedTag addresses. */ @jsonMember({ name: 'Unified', constructor: Hash }) unified?: Hash; - /** The validator hash if this is a validator bid address. */ + /** Validator hash for ValidatorTag addresses. */ @jsonMember({ name: 'Validator', constructor: Hash }) validator?: Hash; - /** The delegator information if this is a delegator bid address. */ - @jsonMember({ name: 'Delegator', constructor: DelegatorInfo }) - delegator?: DelegatorInfo; + /** Delegator account hash for DelegatedAccountTag addresses. */ + @jsonMember({ name: 'DelegatorAccount', constructor: Hash }) + delegatorAccount?: Hash; + + /** Delegator purse address for DelegatedPurseTag addresses. */ + @jsonMember({ name: 'DelegatorPurseAddress', constructor: String }) + delegatorPurseAddress?: string; - /** The credit information if this is a credit bid address. */ - @jsonMember({ name: 'Credit', constructor: CreditInfo }) - credit?: CreditInfo; + /** Era ID for CreditTag addresses. */ + @jsonMember({ name: 'EraId', constructor: Number }) + eraId?: number; + + /** Private field indicating the tag type of the BidAddr. */ + private tag: BidAddrTag; /** - * Constructs a new BidAddr instance. - * @param unified - The unified hash. - * @param validator - The validator hash. - * @param delegator - The delegator information. - * @param credit - The credit information. + * Validates and returns the BidAddrTag from a numeric value. + * @param tag - The numeric tag value to validate. + * @returns The corresponding BidAddrTag. + * @throws {ErrInvalidBidAddrTag} If the tag value is invalid. */ - constructor( - unified?: Hash, - validator?: Hash, - delegator?: { validator: Hash; delegator: Hash }, - credit?: { - validator: Hash; - eraId: number; + static bidAddrTag(tag: number): BidAddrTag { + const addrTag = tag as BidAddrTag; + + if (!Object.values(BidAddrTag).includes(addrTag)) { + throw ErrInvalidBidAddrTag; } - ) { - this.unified = unified; - this.validator = validator; - this.delegator = delegator; - this.credit = credit; + + return addrTag; + } + + /** + * Returns the tag associated with the bid address. + * @returns The BidAddrTag of the bid address. + */ + public getTag(): BidAddrTag { + return this.tag; } /** * Creates a BidAddr from a hexadecimal string. - * @param source - The hexadecimal string. + * @param source - The hexadecimal string representation of the BidAddr. * @returns A new BidAddr instance. * @throws {ErrInvalidBidAddrFormat} If the format is invalid. * @throws {ErrUnexpectedBidAddrTagInBidAddr} If an unexpected tag is encountered. */ static fromHex(source: string): BidAddr { const hexBytes = Buffer.from(source, 'hex'); - if (hexBytes.length < UnifiedOrValidatorAddrLen) - throw ErrInvalidBidAddrFormat; - - const bidAddrTag = BidAddrTagUtils.fromByte(hexBytes[0]); - - if (hexBytes.length === UnifiedOrValidatorAddrLen) { - const hash = Hash.fromBytes(hexBytes.slice(1))?.result; - switch (bidAddrTag) { - case BidAddrTag.Unified: - return new BidAddr(hash); - case BidAddrTag.Validator: - return new BidAddr(undefined, hash); - default: - throw ErrUnexpectedBidAddrTagInBidAddr; - } + + if (hexBytes.length === 0) { + throw new Error('Wrong key length.'); } - const validatorHash = Hash.fromBytes(hexBytes.slice(1, 33)); + const bidAddrTag = BidAddr.bidAddrTag(hexBytes[0]); + const bidAddr = new BidAddr(); + bidAddr.tag = bidAddrTag; + + switch (bidAddrTag) { + case BidAddrTag.UnifiedTag: + case BidAddrTag.ValidatorTag: + if (hexBytes.length !== UnifiedOrValidatorAddrLen) { + throw new Error( + `Wrong key length for ${BidAddrTag[bidAddrTag]} BidAddr. Expected 33 bytes.` + ); + } + const key = + bidAddrTag === BidAddrTag.UnifiedTag ? 'unified' : 'validator'; + bidAddr[key] = Hash.fromBytes(hexBytes.slice(1, 33))?.result; + break; + + case BidAddrTag.DelegatedAccountTag: + case BidAddrTag.ReservedDelegationAccountTag: + case BidAddrTag.UnbondAccountTag: + if (hexBytes.length !== ValidatorHashDelegatorAddrLen) { + throw new Error( + `Wrong key length for ${BidAddrTag[bidAddrTag]} BidAddr. Expected 65 bytes.` + ); + } + bidAddr.validator = Hash.fromBytes(hexBytes.slice(1, 33))?.result; + bidAddr.delegatorAccount = Hash.fromBytes(hexBytes.slice(33))?.result; + break; + + case BidAddrTag.DelegatedPurseTag: + case BidAddrTag.ReservedDelegationPurseTag: + case BidAddrTag.UnbondPurseTag: + if (hexBytes.length !== ValidatorHashDelegatorAddrLen) { + throw new Error( + `Wrong key length for ${BidAddrTag[bidAddrTag]} BidAddr. Expected 65 bytes.` + ); + } + + bidAddr.validator = Hash.fromBytes(hexBytes.slice(1, 33))?.result; + bidAddr.delegatorPurseAddress = new HexBytes( + hexBytes.slice(33) + ).toHex(); + break; - if (hexBytes.length === CreditAddrLen) { - const eraId = hexBytes.readUInt32LE(33); - return new BidAddr(undefined, undefined, undefined, { - validator: validatorHash?.result, - eraId - }); + case BidAddrTag.CreditTag: + if (hexBytes.length !== CreditAddrLen) { + throw new Error( + 'Wrong key length for Credit BidAddr. Expected 41 bytes.' + ); + } + bidAddr.validator = Hash.fromBytes(hexBytes.slice(1, 33))?.result; + bidAddr.eraId = hexBytes.readUInt32LE(33); + break; + + default: + throw new Error(`Wrong BidAddr tag ${bidAddrTag}.`); } - const delegatorHash = Hash.fromBytes(hexBytes.slice(33)); - return new BidAddr(undefined, undefined, { - validator: validatorHash?.result, - delegator: delegatorHash?.result - }); + return bidAddr; } /** @@ -193,47 +176,82 @@ export class BidAddr { const tag = bytes[0]; const rem = bytes.subarray(1); - const bidAddrTag = BidAddrTagUtils.fromByte(tag); - - if (bidAddrTag === BidAddrTag.Unified) { - const { result: unifiedHash, bytes } = Hash.fromBytes(rem); - return { result: new BidAddr(unifiedHash), bytes }; - } + const bidAddrTag = BidAddr.bidAddrTag(tag); + const bidAddr = new BidAddr(); + + const parseHashPair = ( + input: Uint8Array + ): { validator: Hash; delegator: Hash; bytes: Uint8Array } => { + const { result: validator, bytes: nextBytes } = Hash.fromBytes(input); + const { result: delegator, bytes } = Hash.fromBytes(nextBytes!); + return { validator, delegator, bytes }; + }; - if (bidAddrTag === BidAddrTag.Validator) { - const { result: validatorHash, bytes } = Hash.fromBytes(rem); - return { result: new BidAddr(undefined, validatorHash), bytes }; - } + const parseHexBytesPair = ( + input: Uint8Array + ): { validator: Hash; delegator: string; bytes: Uint8Array } => { + const { result: validator, bytes: nextBytes } = Hash.fromBytes(input); + const hexBytes = new HexBytes(nextBytes!); + return { validator, delegator: hexBytes.toHex(), bytes: hexBytes.bytes }; + }; - if (bidAddrTag === BidAddrTag.Delegator) { - const { result: validatorHash, bytes: delegatorBytes } = Hash.fromBytes( - rem - ); - const { result: delegatorHash, bytes } = Hash.fromBytes(delegatorBytes!); - - return { - result: new BidAddr(undefined, undefined, { - validator: validatorHash, - delegator: delegatorHash - }), - bytes - }; + switch (bidAddrTag) { + case BidAddrTag.UnifiedTag: { + const { result: unifiedHash, bytes } = Hash.fromBytes(rem); + bidAddr.unified = unifiedHash; + return { result: bidAddr, bytes }; + } + case BidAddrTag.ValidatorTag: { + const { result: validatorHash, bytes } = Hash.fromBytes(rem); + bidAddr.validator = validatorHash; + return { result: bidAddr, bytes }; + } + case BidAddrTag.DelegatedAccountTag: { + const { validator, delegator, bytes } = parseHashPair(rem); + bidAddr.delegatorAccount = delegator; + bidAddr.validator = validator; + return { result: bidAddr, bytes }; + } + case BidAddrTag.CreditTag: { + const { result: validator, bytes: nextBytes } = Hash.fromBytes(rem); + const eraIdBytes = Uint8Array.from(nextBytes!.subarray(0, 8)); + bidAddr.eraId = BigNumber.from(eraIdBytes.slice().reverse()).toNumber(); + bidAddr.validator = validator; + return { result: bidAddr, bytes: nextBytes }; + } + case BidAddrTag.DelegatedPurseTag: { + const { validator, delegator, bytes } = parseHexBytesPair(rem); + bidAddr.validator = validator; + bidAddr.delegatorPurseAddress = delegator; + return { result: bidAddr, bytes }; + } + case BidAddrTag.ReservedDelegationAccountTag: { + const { validator, delegator, bytes } = parseHashPair(rem); + bidAddr.validator = validator; + bidAddr.delegatorAccount = delegator; + return { result: bidAddr, bytes }; + } + case BidAddrTag.ReservedDelegationPurseTag: { + const { validator, delegator, bytes } = parseHexBytesPair(rem); + bidAddr.validator = validator; + bidAddr.delegatorPurseAddress = delegator; + return { result: bidAddr, bytes }; + } + case BidAddrTag.UnbondAccountTag: { + const { validator, delegator, bytes } = parseHashPair(rem); + bidAddr.validator = validator; + bidAddr.delegatorAccount = delegator; + return { result: bidAddr, bytes }; + } + case BidAddrTag.UnbondPurseTag: { + const { validator, delegator, bytes } = parseHexBytesPair(rem); + bidAddr.validator = validator; + bidAddr.delegatorPurseAddress = delegator; + return { result: bidAddr, bytes }; + } + default: + throw ErrInvalidBidAddrFormat; } - - // Credit - const { result: validatorHash, bytes: validatorBytes } = Hash.fromBytes( - rem - ); - const u64Bytes = Uint8Array.from(validatorBytes!.subarray(0, 8)); - const eraId = BigNumber.from(u64Bytes.slice().reverse()); - - return { - result: new BidAddr(undefined, undefined, undefined, { - validator: validatorHash, - eraId: eraId?.toNumber() - }), - bytes: validatorBytes - }; } /** @@ -245,27 +263,87 @@ export class BidAddr { } /** - * Returns a hexadecimal string representation of the BidAddr. - * @returns The hexadecimal string representation. - * @throws {Error} If the BidAddr type is unexpected. + * Converts the BidAddr to its hexadecimal string representation, ensuring proper formatting. + * @returns The hexadecimal string representation of the BidAddr. + * @throws {Error} If the BidAddr type is unexpected or required fields are missing. */ toHex(): string { - switch (true) { - case !!this.unified: - return `${BidAddrTag.Unified}${this.unified!.toHex()}`; - case !!this.validator: - return `${BidAddrTag.Validator}${this.validator!.toHex()}`; - case !!this.delegator: - return `${ - BidAddrTag.Delegator - }${this.delegator!.validator.toHex()}${this.delegator!.delegator.toHex()}`; - case !!this.credit: - const validatorHex = this.credit!.validator.toHex(); + const tagHex = this.tag.toString(16).padStart(2, '0'); + + switch (this.tag) { + case BidAddrTag.UnifiedTag: + if (!this.unified) { + throw new Error( + `Missing 'unified' field for tag ${BidAddrTag.UnifiedTag}` + ); + } + return `${tagHex}${this.unified.toHex()}`; + case BidAddrTag.ValidatorTag: + if (!this.validator) { + throw new Error( + `Missing 'validator' field for tag ${BidAddrTag.ValidatorTag}` + ); + } + return `${tagHex}${this.validator.toHex()}`; + case BidAddrTag.DelegatedAccountTag: + if (!this.validator || !this.delegatorAccount) { + throw new Error( + `Missing 'validator' or 'delegatorAccount' field for tag ${BidAddrTag.DelegatedAccountTag}` + ); + } + return `${tagHex}${this.validator.toHex()}${this.delegatorAccount.toHex()}`; + case BidAddrTag.DelegatedPurseTag: + if (!this.validator || !this.delegatorPurseAddress) { + throw new Error( + `Missing 'validator' or 'delegatorPurseAddress' field for tag ${BidAddrTag.DelegatedPurseTag}` + ); + } + return `${tagHex}${this.validator.toHex()}${ + this.delegatorPurseAddress + }`; + case BidAddrTag.CreditTag: + if (!this.validator || this.eraId === undefined) { + throw new Error( + `Missing 'validator' or 'eraId' field for tag ${BidAddrTag.CreditTag}` + ); + } const eraIdHex = Buffer.alloc(8); - eraIdHex.writeUInt32LE(this.credit!.eraId, 0); - return `${BidAddrTag.Credit}${validatorHex}${eraIdHex.toString('hex')}`; + eraIdHex.writeUInt32LE(this.eraId, 0); + return `${tagHex}${this.validator.toHex()}${eraIdHex.toString('hex')}`; + case BidAddrTag.ReservedDelegationAccountTag: + if (!this.validator || !this.delegatorAccount) { + throw new Error( + `Missing 'validator' or 'delegatorAccount' field for tag ${BidAddrTag.ReservedDelegationAccountTag}` + ); + } + return `${tagHex}${this.validator.toHex()}${this.delegatorAccount.toHex()}`; + case BidAddrTag.ReservedDelegationPurseTag: + if (!this.validator || !this.delegatorPurseAddress) { + throw new Error( + `Missing 'validator' or 'delegatorPurseAddress' field for tag ${BidAddrTag.ReservedDelegationPurseTag}` + ); + } + return `${tagHex}${this.validator.toHex()}${ + this.delegatorPurseAddress + }`; + case BidAddrTag.UnbondAccountTag: + if (!this.validator || !this.delegatorAccount) { + throw new Error( + `Missing 'validator' or 'delegatorAccount' field for tag ${BidAddrTag.UnbondAccountTag}` + ); + } + return `${tagHex}${this.validator.toHex()}${this.delegatorAccount.toHex()}`; + case BidAddrTag.UnbondPurseTag: + if (!this.validator || !this.delegatorPurseAddress) { + throw new Error( + `Missing 'validator' or 'delegatorPurseAddress' field for tag ${BidAddrTag.UnbondPurseTag}` + ); + } + return `${tagHex}${this.validator.toHex()}${ + this.delegatorPurseAddress + }`; default: - throw new Error('Unexpected BidAddr type'); + throw new Error(`Unexpected BidAddr type: ${this.tag}`); } } @@ -275,60 +353,109 @@ export class BidAddr { * @throws {Error} If the BidAddr type is unexpected. */ toBytes(): Uint8Array { - const typeByte = this.getTypeByte(); - let result = Buffer.alloc(0); + const typeByte = this.getTag(); + + const concatBuffers = (buffers: Buffer[]): Buffer => Buffer.concat(buffers); + + const createBuffer = (tag: number, ...parts: Uint8Array[]): Buffer => { + return concatBuffers([Buffer.from([tag]), ...parts.map(Buffer.from)]); + }; switch (typeByte) { - case BidAddrTag.Unified: - result = Buffer.concat([ - Buffer.from([typeByte]), - this.unified!.toBytes() - ]); - break; - case BidAddrTag.Validator: - result = Buffer.concat([ - Buffer.from([typeByte]), - this.validator!.toBytes() - ]); + case BidAddrTag.UnifiedTag: + return new Uint8Array(createBuffer(typeByte, this.unified!.toBytes())); + case BidAddrTag.ValidatorTag: + return new Uint8Array( + createBuffer(typeByte, this.validator!.toBytes()) + ); + case BidAddrTag.DelegatedAccountTag: + if (this.delegatorAccount && this.validator) { + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + this.delegatorAccount.toBytes() + ) + ); + } break; - case BidAddrTag.Delegator: - if (this.delegator) { - result = Buffer.concat([ - Buffer.from([typeByte]), - this.delegator.validator.toBytes(), - this.delegator.delegator.toBytes() - ]); + case BidAddrTag.DelegatedPurseTag: + if (this.delegatorPurseAddress && this.validator) { + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + toBytesString(this.delegatorPurseAddress) + ) + ); } break; - case BidAddrTag.Credit: - if (this.credit) { + case BidAddrTag.CreditTag: + if (this.validator && this.eraId) { const eraIdBuffer = Buffer.alloc(8); - eraIdBuffer.writeBigUInt64LE(BigInt(this.credit.eraId), 0); - result = Buffer.concat([ - Buffer.from([typeByte]), - this.credit.validator.toBytes(), - eraIdBuffer - ]); + eraIdBuffer.writeBigUInt64LE(BigInt(this.eraId), 0); + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + new Uint8Array(eraIdBuffer) + ) + ); + } + break; + case BidAddrTag.ReservedDelegationAccountTag: + if (this.validator && this.delegatorAccount) { + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + this.delegatorAccount.toBytes() + ) + ); + } + break; + case BidAddrTag.ReservedDelegationPurseTag: + if (this.delegatorPurseAddress && this.validator) { + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + toBytesString(this.delegatorPurseAddress) + ) + ); + } + break; + case BidAddrTag.UnbondAccountTag: + if (this.validator && this.delegatorAccount) { + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + this.delegatorAccount.toBytes() + ) + ); + } + break; + case BidAddrTag.UnbondPurseTag: + if (this.delegatorPurseAddress && this.validator) { + return new Uint8Array( + createBuffer( + typeByte, + this.validator.toBytes(), + toBytesString(this.delegatorPurseAddress) + ) + ); } break; default: - throw new Error('Unexpected BidAddr type'); + throw new Error( + `Unexpected BidAddr type: Unknown type byte ${typeByte}` + ); } - return new Uint8Array(result); - } - - /** - * Gets the type byte of the BidAddr. - * @returns The type byte. - * @throws {Error} If the BidAddr type is not set. - */ - private getTypeByte(): number { - if (this.unified) return BidAddrTag.Unified; - if (this.validator) return BidAddrTag.Validator; - if (this.delegator) return BidAddrTag.Delegator; - if (this.credit) return BidAddrTag.Credit; - throw new Error('BidAddr type is not set'); + throw new Error( + `Unexpected BidAddr type: Missing required fields for type byte ${typeByte}` + ); } /** From 2625dd35a89c53a060e3f510131b73b76251fc7d Mon Sep 17 00:00:00 2001 From: Oleksandr Myshchyshyn Date: Tue, 28 Jan 2025 19:48:13 +0200 Subject: [PATCH 2/2] Added tag creation --- src/types/key/BidAddr.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/key/BidAddr.ts b/src/types/key/BidAddr.ts index 5e14de9e2..ef9a7e0d0 100644 --- a/src/types/key/BidAddr.ts +++ b/src/types/key/BidAddr.ts @@ -178,6 +178,7 @@ export class BidAddr { const rem = bytes.subarray(1); const bidAddrTag = BidAddr.bidAddrTag(tag); const bidAddr = new BidAddr(); + bidAddr.tag = bidAddrTag; const parseHashPair = ( input: Uint8Array