From b733284eacbc8211b0f3b7bb2b116dd279d562df Mon Sep 17 00:00:00 2001 From: Jan Hoffmann Date: Mon, 10 Jun 2024 06:29:19 +0200 Subject: [PATCH] feat: added parsing for missing types --- src/lib/CLValue/AccountHash.ts | 3 +- src/lib/CLValue/Key.ts | 152 ++++++++++++++--- src/lib/CLValue/KeyVariants.ts | 288 +++++++++++++++++++++++++++++++-- src/lib/CLValue/URef.ts | 14 +- src/lib/CLValue/constants.ts | 21 +++ 5 files changed, 433 insertions(+), 45 deletions(-) diff --git a/src/lib/CLValue/AccountHash.ts b/src/lib/CLValue/AccountHash.ts index a28c7f027..2226c4f3e 100644 --- a/src/lib/CLValue/AccountHash.ts +++ b/src/lib/CLValue/AccountHash.ts @@ -12,6 +12,7 @@ import { ACCOUNT_HASH_TYPE, CLByteArrayType, ACCOUNT_HASH_LENGTH, + ACCOUNT_HASH_PREFIX, KeyTag } from './index'; @@ -44,8 +45,6 @@ export class CLAccountHashBytesParser extends CLValueBytesParsers { } } -const ACCOUNT_HASH_PREFIX = 'account-hash'; - /** A cryptographic public key. */ export class CLAccountHash extends CLValue implements CLKeyVariant { data: Uint8Array; diff --git a/src/lib/CLValue/Key.ts b/src/lib/CLValue/Key.ts index 0c743e8e2..36464fd08 100644 --- a/src/lib/CLValue/Key.ts +++ b/src/lib/CLValue/Key.ts @@ -16,8 +16,33 @@ import { CLValueBytesParsers, CLValueParsers, resultHelper, + KeyHash, HashParser, + KeyTransferAddr, + DeployHash, + EraInfo, + Balance, + KeyBid, + Withdraw, + KeyDictionary, + KeySystemEntityRegistry, + KeyEraSummary, + KeyUnbond, + ACCOUNT_HASH_PREFIX, + HASH_PREFIX, + UREF_PREFIX, + TRANSFER_PREFIX, + DEPLOY_HASH_PREFIX, + ERA_INFO_PREFIX, + BALANCE_PREFIX, + BID_PREFIX, + WITHDRAW_PREFIX, + DICTIONARY_PREFIX, + SYSTEM_ENTITY_REGISTRY_PREFIX, + ERA_SUMMARY_PREFIX, + UNBOND_PREFIX } from './index'; +import { toBytesNumber } from '../ByteConverters'; import { KEY_TYPE, CLTypeTag } from './constants'; export class CLKeyType extends CLType { @@ -27,26 +52,74 @@ export class CLKeyType extends CLType { export class CLKeyBytesParser extends CLValueBytesParsers { toBytes(value: CLKey): ToBytesResult { - if (value.isAccount()) { - return Ok( - concat([ - Uint8Array.from([KeyTag.Account]), - new CLAccountHashBytesParser() - .toBytes(value.data as CLAccountHash) - .unwrap() - ]) - ); - } - if (value.isHash()) { - return Ok(concat([Uint8Array.from([KeyTag.Hash]), value.data.data])); - } - if (value.isURef()) { - return Ok( - concat([ - Uint8Array.from([KeyTag.URef]), - CLValueParsers.toBytes(value.data as CLURef).unwrap() - ]) - ); + switch (value.data.keyVariant) { + case KeyTag.Account: + return Ok( + concat([ + Uint8Array.from([KeyTag.Account]), + new CLAccountHashBytesParser() + .toBytes(value.data as CLAccountHash) + .unwrap() + ]) + ); + case KeyTag.Hash: + return Ok(concat([Uint8Array.from([KeyTag.Hash]), value.data.data])); + case KeyTag.URef: + return Ok( + concat([ + Uint8Array.from([KeyTag.URef]), + CLValueParsers.toBytes(value.data as CLURef).unwrap() + ]) + ); + case KeyTag.Transfer: + return Ok( + concat([Uint8Array.from([KeyTag.Transfer]), value.data.data]) + ); + case KeyTag.DeployInfo: + return Ok( + concat([Uint8Array.from([KeyTag.DeployInfo]), value.data.data]) + ); + case KeyTag.EraInfo: + return Ok( + concat([ + Uint8Array.from([KeyTag.DeployInfo]), + toBytesNumber(64, false)(value.data.data) + ]) + ); + case KeyTag.Balance: + return Ok(concat([Uint8Array.from([KeyTag.Balance]), value.data.data])); + case KeyTag.Bid: + return Ok(concat([Uint8Array.from([KeyTag.Bid]), value.data.data])); + case KeyTag.Withdraw: + return Ok( + concat([Uint8Array.from([KeyTag.Withdraw]), value.data.data]) + ); + case KeyTag.Dictionary: + return Ok( + concat([Uint8Array.from([KeyTag.Dictionary]), value.data.data]) + ); + case KeyTag.SystemEntityRegistry: + return Ok( + concat([Uint8Array.from([KeyTag.SystemEntityRegistry]), value.data.data]) + ); + case KeyTag.EraSummary: + return Ok( + concat([Uint8Array.from([KeyTag.EraSummary]), value.data.data]) + ); + case KeyTag.ChainspecRegistry: + return Ok( + concat([Uint8Array.from([KeyTag.ChainspecRegistry]), value.data.data]) + ); + case KeyTag.ChecksumRegistry: + return Ok( + concat([Uint8Array.from([KeyTag.ChecksumRegistry]), value.data.data]) + ); + + throw new Error('TODO: Implement parsing and serializing'); + default: + throw new Error( + `Problem serializing keyVariant: ${value.data.keyVariant}` + ); } throw new Error('Unknown byte types'); } @@ -135,4 +208,43 @@ export class CLKey extends CLValue { isAccount(): boolean { return this.data.keyVariant === KeyTag.Account; } + + static fromFormattedString(input: string): CLKey { + const lastDashIndex = input.lastIndexOf('-'); + if (lastDashIndex >= 0) { + const prefix = input.slice(0, lastDashIndex); + + switch (prefix) { + case ACCOUNT_HASH_PREFIX: + return new CLKey(CLAccountHash.fromFormattedString(input)); + case HASH_PREFIX: + return new CLKey(KeyHash.fromFormattedString(input)); + case UREF_PREFIX: + return new CLKey(CLURef.fromFormattedString(input)); + case TRANSFER_PREFIX: + return new CLKey(KeyTransferAddr.fromFormattedString(input)); + case DEPLOY_HASH_PREFIX: + return new CLKey(DeployHash.fromFormattedString(input)); + case ERA_INFO_PREFIX: + return new CLKey(EraInfo.fromFormattedString(input)); + case BALANCE_PREFIX: + return new CLKey(Balance.fromFormattedString(input)); + case BID_PREFIX: + return new CLKey(KeyBid.fromFormattedString(input)); + case WITHDRAW_PREFIX: + return new CLKey(Withdraw.fromFormattedString(input)); + case DICTIONARY_PREFIX: + return new CLKey(KeyDictionary.fromFormattedString(input)); + case SYSTEM_ENTITY_REGISTRY_PREFIX: + return new CLKey(KeySystemEntityRegistry.fromFormattedString(input)); + case ERA_SUMMARY_PREFIX: + return new CLKey(KeyEraSummary.fromFormattedString(input)); + case UNBOND_PREFIX: + return new CLKey(KeyUnbond.fromFormattedString(input)); + default: + throw new Error('Unsupported prefix'); + } + } + throw Error(`Wrong string format`); + } } diff --git a/src/lib/CLValue/KeyVariants.ts b/src/lib/CLValue/KeyVariants.ts index f916711e6..ed5e76c76 100644 --- a/src/lib/CLValue/KeyVariants.ts +++ b/src/lib/CLValue/KeyVariants.ts @@ -1,10 +1,23 @@ import { Ok } from 'ts-results'; +import { BigNumberish, BigNumber } from '@ethersproject/bignumber'; import { KeyTag, ResultAndRemainder, resultHelper, CLErrorCodes, - CLValueBytesParsers + CLValueBytesParsers, + UREF_ADDR_LENGTH, + HASH_PREFIX, + TRANSFER_PREFIX, + DEPLOY_HASH_PREFIX, + ERA_INFO_PREFIX, + BALANCE_PREFIX, + BID_PREFIX, + WITHDRAW_PREFIX, + DICTIONARY_PREFIX, + SYSTEM_ENTITY_REGISTRY_PREFIX, + ERA_SUMMARY_PREFIX, + UNBOND_PREFIX } from './index'; import { decodeBase16, encodeBase16 } from '../Conversions'; @@ -15,7 +28,7 @@ import { decodeBase16, encodeBase16 } from '../Conversions'; // - serialize when inside a Key export abstract class CLKeyVariant { abstract keyVariant: KeyTag; - abstract data: Uint8Array; + abstract data: any; value(): any { return this.data; @@ -25,29 +38,25 @@ export abstract class CLKeyVariant { abstract toFormattedString(): string; - static fromFormattedString(hexStr: string): CLKeyVariant { + static fromFormattedStringing(hexStr: string): CLKeyVariant { throw Error( `Trying to deserialize KeyVariant - unknown string provided: ${hexStr}` ); } } -export const HASH_PREFIX = 'hash'; -export const TRANSFER_PREFIX = 'transfer'; -export const DEPLOY_HASH_PREFIX = 'deploy-hash'; - const KEY_HASH_LENGTH = 32; export const HashParser = { fromBytesWithRemainder: ( bytes: Uint8Array - ): ResultAndRemainder => { - const hash = new Hash(bytes.subarray(0, KEY_HASH_LENGTH)); + ): ResultAndRemainder => { + const hash = new KeyHash(bytes.subarray(0, KEY_HASH_LENGTH)); return resultHelper(Ok(hash), bytes.subarray(KEY_HASH_LENGTH)); } }; -export class Hash implements CLKeyVariant { +export class KeyHash implements CLKeyVariant { keyVariant = KeyTag.Hash; prefix = HASH_PREFIX; @@ -65,7 +74,7 @@ export class Hash implements CLKeyVariant { return `${HASH_PREFIX}-${this.toString()}`; } - static fromFormattedStr(input: string): Hash { + static fromFormattedString(input: string): KeyHash { if (!input.startsWith(`${HASH_PREFIX}-`)) { throw new Error("Prefix is not 'uref-'"); } @@ -73,11 +82,11 @@ export class Hash implements CLKeyVariant { const hashStr = input.substring(`${HASH_PREFIX}-`.length + 1); const hashBytes = decodeBase16(hashStr); - return new Hash(hashBytes); + return new KeyHash(hashBytes); } } -export class TransferAddr implements CLKeyVariant { +export class KeyTransferAddr implements CLKeyVariant { keyVariant = KeyTag.Transfer; prefix = TRANSFER_PREFIX; @@ -95,7 +104,7 @@ export class TransferAddr implements CLKeyVariant { return `${TRANSFER_PREFIX}-${this.toString()}`; } - static fromFormattedStr(input: string): Hash { + static fromFormattedString(input: string): KeyTransferAddr { if (!input.startsWith(`${TRANSFER_PREFIX}-`)) { throw new Error(`Prefix is not ${TRANSFER_PREFIX}`); } @@ -103,7 +112,7 @@ export class TransferAddr implements CLKeyVariant { const hashStr = input.substring(`${TRANSFER_PREFIX}-`.length + 1); const hashBytes = decodeBase16(hashStr); - return new TransferAddr(hashBytes); + return new KeyTransferAddr(hashBytes); } } @@ -125,7 +134,7 @@ export class DeployHash implements CLKeyVariant { return `${DEPLOY_HASH_PREFIX}-${this.toString()}`; } - static fromFormattedStr(input: string): DeployHash { + static fromFormattedString(input: string): DeployHash { if (!input.startsWith(`${DEPLOY_HASH_PREFIX}-`)) { throw new Error(`Prefix is not ${DEPLOY_HASH_PREFIX}`); } @@ -137,4 +146,251 @@ export class DeployHash implements CLKeyVariant { } } +export class EraInfo implements CLKeyVariant { + keyVariant = KeyTag.EraInfo; + prefix = ERA_INFO_PREFIX; + data: BigNumber; + + constructor(value: BigNumberish) { + this.data = BigNumber.from(value); + } + + value(): any { + return this.data; + } + + toString() { + return this.value(); + } + + toFormattedString() { + return `${ERA_INFO_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): EraInfo { + if (!input.startsWith(`${ERA_INFO_PREFIX}-`)) { + throw new Error(`Prefix is not ${ERA_INFO_PREFIX}`); + } + + const hashStr = input.substring(`${ERA_INFO_PREFIX}-`.length + 1); + const hashBytes = decodeBase16(hashStr); + + return new EraInfo(hashBytes); + } +} + +export class Balance implements CLKeyVariant { + keyVariant = KeyTag.Balance; + prefix = BALANCE_PREFIX; + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${BALANCE_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): DeployHash { + if (!input.startsWith(`${BALANCE_PREFIX}-`)) { + throw new Error(`Prefix is not ${BALANCE_PREFIX}`); + } + + const hashStr = input.substring(`${BALANCE_PREFIX}-`.length + 1); + const hashBytes = decodeBase16(hashStr); + + return new Balance(hashBytes); + } +} + +export class KeyBid implements CLKeyVariant { + keyVariant = KeyTag.Bid; + prefix = BID_PREFIX; + + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${BID_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): KeyBid { + if (!input.startsWith(`${BID_PREFIX}-`)) { + throw new Error(`Prefix is not ${BID_PREFIX}`); + } + + const hashStr = input.substring(`${BID_PREFIX}-`.length + 1); + const hashBytes = decodeBase16(hashStr); + + return new KeyBid(hashBytes); + } +} + +export class Withdraw implements CLKeyVariant { + keyVariant = KeyTag.Withdraw; + prefix = WITHDRAW_PREFIX; + + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${WITHDRAW_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): DeployHash { + if (!input.startsWith(`${WITHDRAW_PREFIX}-`)) { + throw new Error(`Prefix is not ${WITHDRAW_PREFIX}`); + } + + const hashStr = input.substring(`${WITHDRAW_PREFIX}-`.length + 1); + const hashBytes = decodeBase16(hashStr); + + return new Withdraw(hashBytes); + } +} + +export class KeyDictionary implements CLKeyVariant { + keyVariant = KeyTag.Dictionary; + prefix = DICTIONARY_PREFIX; + + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${DICTIONARY_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): DeployHash { + if (!input.startsWith(`${DICTIONARY_PREFIX}-`)) { + throw new Error(`Prefix is not ${DICTIONARY_PREFIX}`); + } + + const hashStr = input.substring(`${DICTIONARY_PREFIX}-`.length + 1); + const hashBytes = decodeBase16(hashStr); + + return new KeyDictionary(hashBytes); + } +} + +export class KeySystemEntityRegistry implements CLKeyVariant { + keyVariant = KeyTag.SystemEntityRegistry; + prefix = DICTIONARY_PREFIX; + + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${SYSTEM_ENTITY_REGISTRY_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): DeployHash { + if (!input.startsWith(`${SYSTEM_ENTITY_REGISTRY_PREFIX}-`)) { + throw new Error(`Prefix is not ${SYSTEM_ENTITY_REGISTRY_PREFIX}`); + } + + const hashStr = input.substring( + `${SYSTEM_ENTITY_REGISTRY_PREFIX}-`.length + 1 + ); + const hashBytes = decodeBase16(hashStr); + + return new KeySystemEntityRegistry(hashBytes); + } +} + +export class KeyEraSummary implements CLKeyVariant { + keyVariant = KeyTag.EraSummary; + prefix = ERA_SUMMARY_PREFIX; + + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${ERA_SUMMARY_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): DeployHash { + if (!input.startsWith(`${ERA_SUMMARY_PREFIX}-`)) { + throw new Error(`Prefix is not ${ERA_SUMMARY_PREFIX}`); + } + + const hashStr = input.substring( + `${ERA_SUMMARY_PREFIX}-`.length + 1 + ); + const hashBytes = decodeBase16(hashStr); + + return new KeyEraSummary(hashBytes); + } +} + +export class KeyUnbond implements CLKeyVariant { + keyVariant = KeyTag.Unbond; + prefix = UNBOND_PREFIX; + + constructor(public data: Uint8Array) {} + + value(): any { + return this.data; + } + + toString() { + return encodeBase16(this.data); + } + + toFormattedString() { + return `${UNBOND_PREFIX}-${this.toString()}`; + } + + static fromFormattedString(input: string): DeployHash { + if (!input.startsWith(`${UNBOND_PREFIX}-`)) { + throw new Error(`Prefix is not ${UNBOND_PREFIX}`); + } + + const hashStr = input.substring( + `${UNBOND_PREFIX}-`.length + 1 + ); + const hashBytes = decodeBase16(hashStr); + + return new KeyUnbond(hashBytes); + } +} diff --git a/src/lib/CLValue/URef.ts b/src/lib/CLValue/URef.ts index 73fb36f30..928bb470c 100644 --- a/src/lib/CLValue/URef.ts +++ b/src/lib/CLValue/URef.ts @@ -11,7 +11,8 @@ import { ResultAndRemainder, ToBytesResult, resultHelper, - padNum + padNum, + UREF_PREFIX } from './index'; import { UREF_TYPE, CLTypeTag } from './constants'; import { decodeBase16, encodeBase16 } from '../Conversions'; @@ -40,12 +41,11 @@ export class CLURefType extends CLType { tag = CLTypeTag.URef; } -const FORMATTED_STRING_PREFIX = 'uref'; /** * Length of [[URef]] address field. * @internal */ -const UREF_ADDR_LENGTH = 32; +export const UREF_ADDR_LENGTH = 32; /** * Length of [[ACCESS_RIGHT]] field. * @internal @@ -102,12 +102,12 @@ export class CLURef extends CLValue implements CLKeyVariant { /** * Parses a casper-client supported string formatted argument into a `URef`. */ - static fromFormattedStr(input: string): CLURef { - if (!input.startsWith(`${FORMATTED_STRING_PREFIX}-`)) { + static fromFormattedString(input: string): CLURef { + if (!input.startsWith(`${UREF_PREFIX}-`)) { throw new Error("Prefix is not 'uref-'"); } const parts = input - .substring(`${FORMATTED_STRING_PREFIX}-`.length) + .substring(`${UREF_PREFIX}-`.length) .split('-', 2); if (parts.length !== 2) { throw new Error('No access rights as suffix'); @@ -127,7 +127,7 @@ export class CLURef extends CLValue implements CLKeyVariant { } toFormattedString(): string { - return [FORMATTED_STRING_PREFIX, this.toString()].join('-'); + return [UREF_PREFIX, this.toString()].join('-'); } toJSON(): string { diff --git a/src/lib/CLValue/constants.ts b/src/lib/CLValue/constants.ts index c5dea1bd2..3f12f8017 100644 --- a/src/lib/CLValue/constants.ts +++ b/src/lib/CLValue/constants.ts @@ -35,6 +35,27 @@ export enum KeyTag { BalanceHold = 22 } +export const ACCOUNT_HASH_PREFIX = 'account-hash'; +export const HASH_PREFIX = 'hash'; +export const UREF_PREFIX = 'uref'; +export const TRANSFER_PREFIX = 'transfer'; +export const DEPLOY_HASH_PREFIX = 'deploy-hash'; +export const ERA_INFO_PREFIX = 'era'; +export const BALANCE_PREFIX = 'balance'; +export const BID_PREFIX = 'bid'; +export const WITHDRAW_PREFIX = 'withdraw'; +export const DICTIONARY_PREFIX = 'dictionary'; +export const SYSTEM_ENTITY_REGISTRY_PREFIX = 'system-entity-registry'; +export const ERA_SUMMARY_PREFIX = 'era-summary'; +export const UNBOND_PREFIX = 'unbond'; +export const CHAINSPEC_REGISTRY_PREFIX = 'chainspec-registry'; +export const CHECKSUM_REGISTRY_PREFIX = 'checksum-registry'; +export const BID_ADDR_PREFIX = 'bid-addr'; +export const PACKAGE_PREFIX = 'package'; +export const BLOCK_GLOBAL_TIME_PREFIX = 'block-time'; +export const BLOCK_GLOBAL_MESSAGE_COUNT_PREFIX= 'block-message-count'; + + /** * Casper types, i.e. types which can be stored and manipulated by smart contracts. *