diff --git a/.github/workflows/publish-casper-client-sdk.yml b/.github/workflows/publish-casper-client-sdk.yml index b401977d6..2c7dd6382 100644 --- a/.github/workflows/publish-casper-client-sdk.yml +++ b/.github/workflows/publish-casper-client-sdk.yml @@ -22,7 +22,7 @@ jobs: - name: Set release tag run: | if [ ${{ github.event.release.target_commitish }} == 'feat-5.0.0' ]; then - echo "release_tag=5.0.0-rc1" >> $GITHUB_ENV + echo "release_tag=condor" >> $GITHUB_ENV else echo "release_tag=latest" >> $GITHUB_ENV fi diff --git a/src/index.ts b/src/index.ts index 0bbd8c375..1287a2b7b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ +export * from './types'; export * from './rpc'; export * from './sse'; -export * from './types'; diff --git a/src/rpc/request.ts b/src/rpc/request.ts index 40de16e2a..7db9495ed 100644 --- a/src/rpc/request.ts +++ b/src/rpc/request.ts @@ -44,7 +44,7 @@ export class RpcRequest { @jsonMember({ constructor: String }) version: string; - @jsonMember({ isRequired: false, constructor: IDValue }) + @jsonMember(() => ({ constructor: IDValue, isRequired: false })) id?: IDValue; @jsonMember({ constructor: String }) @@ -137,7 +137,7 @@ export class ParamQueryGlobalStateID { @jsonObject export class ParamTransactionHash { - @jsonMember({ isRequired: false, constructor: TransactionHash }) + @jsonMember(() => ({ constructor: TransactionHash, isRequired: false })) transactionHash?: TransactionHash; @jsonMember({ isRequired: false, constructor: Boolean }) @@ -199,10 +199,10 @@ export class ParamGetStateEntity { @jsonObject export class AccountIdentifier { - @jsonMember({ isRequired: false, constructor: AccountHash }) + @jsonMember(() => ({ constructor: AccountHash, isRequired: false })) accountHash?: AccountHash; - @jsonMember({ isRequired: false, constructor: PublicKey }) + @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) publicKey?: PublicKey; constructor(accountHash?: AccountHash, publicKey?: PublicKey) { @@ -213,13 +213,13 @@ export class AccountIdentifier { @jsonObject export class EntityIdentifier { - @jsonMember({ isRequired: false, constructor: AccountHash }) + @jsonMember(() => ({ constructor: AccountHash, isRequired: false })) accountHash?: AccountHash; - @jsonMember({ isRequired: false, constructor: PublicKey }) + @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) publicKey?: PublicKey; - @jsonMember({ isRequired: false, constructor: EntityAddr }) + @jsonMember(() => ({ constructor: EntityAddr, isRequired: false })) entityAddr?: EntityAddr; constructor( @@ -247,7 +247,7 @@ export class EntityIdentifier { @jsonObject export class PutDeployRequest { - @jsonMember({ constructor: Deploy }) + @jsonMember(() => ({ constructor: Deploy })) deploy: Deploy; constructor(deploy: Deploy) { @@ -257,7 +257,7 @@ export class PutDeployRequest { @jsonObject export class PutTransactionRequest { - @jsonMember({ constructor: TransactionWrapper }) + @jsonMember(() => ({ constructor: TransactionWrapper })) transaction: TransactionWrapper; constructor(transaction: TransactionWrapper) { @@ -414,7 +414,7 @@ export class ParamDictionaryIdentifierURef { @jsonObject export class SpeculativeExecParams { - @jsonMember({ constructor: Deploy }) + @jsonMember(() => ({ constructor: Deploy })) deploy: Deploy; @jsonMember({ isRequired: false, constructor: BlockIdentifier }) @@ -428,16 +428,16 @@ export class SpeculativeExecParams { @jsonObject export class PurseIdentifier { - @jsonMember({ isRequired: false, constructor: PublicKey }) + @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) mainPurseUnderPublicKey?: PublicKey; - @jsonMember({ isRequired: false, constructor: AccountHash }) + @jsonMember(() => ({ constructor: AccountHash, isRequired: false })) mainPurseUnderAccountHash?: AccountHash; - @jsonMember({ isRequired: false, constructor: EntityAddr }) + @jsonMember(() => ({ constructor: EntityAddr, isRequired: false })) mainPurseUnderEntityAddr?: EntityAddr; - @jsonMember({ isRequired: false, constructor: URef }) + @jsonMember(() => ({ constructor: URef, isRequired: false })) purseUref?: URef; constructor( @@ -505,7 +505,7 @@ export class QueryBalanceDetailsRequest { @jsonObject export class InfoGetRewardRequest { - @jsonMember({ constructor: PublicKey }) + @jsonMember(() => ({ constructor: PublicKey, isRequired: false })) validator: PublicKey; @jsonMember({ isRequired: false, constructor: PublicKey }) diff --git a/src/rpc/response.ts b/src/rpc/response.ts index c33975e0b..a634b6df0 100644 --- a/src/rpc/response.ts +++ b/src/rpc/response.ts @@ -41,13 +41,13 @@ export class RpcResponse { @jsonMember({ name: 'jsonrpc', constructor: String }) version: string; - @jsonMember({ name: 'id', constructor: IDValue }) + @jsonMember(() => ({ constructor: IDValue, name: 'id' })) id?: IDValue; @jsonMember({ name: 'result', constructor: String }) result: string; - @jsonMember({ name: 'error', constructor: RpcError }) + @jsonMember(() => ({ name: 'error', constructor: RpcError })) error?: RpcError; } @@ -56,7 +56,7 @@ export class StateGetAuctionInfoResult { @jsonMember({ name: 'api_version', constructor: String }) version: string; - @jsonMember({ name: 'auction_state', constructor: AuctionState }) + @jsonMember(() => ({ name: 'auction_state', constructor: AuctionState })) auctionState: AuctionState; rawJSON?: string; @@ -67,7 +67,7 @@ export class StateGetBalanceResult { @jsonMember({ name: 'api_version', constructor: String }) apiVersion: string; - @jsonMember({ name: 'balance_value', constructor: CLValueUInt512 }) + @jsonMember(() => ({ name: 'balance_value', constructor: CLValueUInt512 })) balanceValue: CLValueUInt512; public rawJSON: string; diff --git a/src/sse/event_parser.ts b/src/sse/event_parser.ts index ff137be21..47c8f9fba 100644 --- a/src/sse/event_parser.ts +++ b/src/sse/event_parser.ts @@ -1,8 +1,10 @@ -import { AllEventsNames, EventType, RawEvent } from "./event"; -import { ErrUnknownEventType } from "./errors"; +import { concat } from '@ethersproject/bytes'; -const headerID = "id:"; -const headerData = "data:"; +import { AllEventsNames, EventType, RawEvent } from './event'; +import { ErrUnknownEventType } from './errors'; + +const headerID = 'id:'; +const headerData = 'data:'; export class EventParser { private eventsToParse: Map = new Map(); @@ -29,25 +31,38 @@ export class EventParser { if (this.hasPrefix(line, headerID)) { eventID = this.trimPrefix(headerID.length, line); } else if (this.hasPrefix(line, headerData)) { + const lineData = this.trimPrefix(headerData.length, line); if (eventData) { - eventData = new Uint8Array([...eventData, ...this.trimPrefix(headerData.length, line), '\n'.charCodeAt(0)]); + eventData = concat([ + eventData, + lineData, + Uint8Array.of('\n'.charCodeAt(0)) + ]); } else { - eventData = this.trimPrefix(headerData.length, line); + eventData = lineData; } } } + return this.parseEventType(eventID, eventData); } - parseEventType(eventID: Uint8Array | undefined, eventData: Uint8Array | undefined): RawEvent | Error { + parseEventType( + eventID: Uint8Array | undefined, + eventData: Uint8Array | undefined + ): RawEvent | Error { if (!eventID || !eventData) { - return new Error("Missing eventID or eventData"); + return new Error('Missing eventID or eventData'); } let eventType: EventType | undefined; - const trimmedData = this.trimPrefix("{\"".length, eventData); + const trimmedData = this.trimPrefix('{"'.length, eventData); - for (const [eType, typeName] of this.eventsToParse.entries()) { + // Convert entries to an array to allow iteration in ES5 + const eventEntries = Array.from(this.eventsToParse.entries()); + + for (let i = 0; i < eventEntries.length; i++) { + const [eType, typeName] = eventEntries[i]; if (this.hasPrefix(trimmedData, typeName)) { eventType = eType; break; @@ -59,24 +74,31 @@ export class EventParser { } if (eventType === EventType.APIVersionEventType) { - return new RawEvent(eventType, eventData.toString(), Number(eventID.toString())); + const idString = Buffer.from(eventID).toString(); + return new RawEvent( + eventType, + Buffer.from(eventData).toString(), + Number(idString) + ); } const parsedID = parseInt(new TextDecoder().decode(eventID), 10); if (isNaN(parsedID)) { - return new Error("Error parsing event id"); + return new Error('Error parsing event id'); } - return new RawEvent(eventType, eventData.toString(), parsedID); + return new RawEvent(eventType, Buffer.from(eventData).toString(), parsedID); } private hasPrefix(line: Uint8Array, prefix: string): boolean { - return String.fromCharCode(...line.slice(0, prefix.length)) === prefix; + const lineString = Buffer.from(line.slice(0, prefix.length)).toString(); + return lineString === prefix; } private splitData(data: Uint8Array | string): Uint8Array[] { - const strData = typeof data === 'string' ? data : new TextDecoder().decode(data); - return strData.split(/\r?\n/).map((line) => new TextEncoder().encode(line)); + const strData = + typeof data === 'string' ? data : new TextDecoder().decode(data); + return strData.split(/\r?\n/).map(line => new TextEncoder().encode(line)); } trimPrefix(size: number, data: Uint8Array): Uint8Array { @@ -97,4 +119,3 @@ export class EventParser { return data; } } - diff --git a/src/sse/stream_reader.ts b/src/sse/stream_reader.ts index a1631d2e1..1ac992db7 100644 --- a/src/sse/stream_reader.ts +++ b/src/sse/stream_reader.ts @@ -24,8 +24,13 @@ export class EventStreamReader { const { value, done } = await reader.next(); if (done) break; - buffer = new Uint8Array([...buffer, ...value]); + // Concatenate new data to buffer using typed array allocation + const tempBuffer = new Uint8Array(buffer.length + value.length); + tempBuffer.set(buffer, 0); + tempBuffer.set(value, buffer.length); + buffer = tempBuffer; + // Enforce max buffer size limit if (buffer.length > this.maxBufferSize) { buffer = buffer.slice(-this.maxBufferSize); } @@ -33,6 +38,7 @@ export class EventStreamReader { let splitIndex: number; let delimiterLength: number; + // Process each complete section separated by double newlines while ( (([splitIndex, delimiterLength] = containsDoubleNewline(buffer)), splitIndex >= 0) @@ -41,6 +47,8 @@ export class EventStreamReader { buffer = buffer.slice(splitIndex + delimiterLength); } } + + // Yield remaining buffer if any data remains if (buffer.length > 0) yield buffer; } diff --git a/src/types/Block.ts b/src/types/Block.ts index aeb3180ac..5d1079073 100644 --- a/src/types/Block.ts +++ b/src/types/Block.ts @@ -13,37 +13,37 @@ import { HexBytes } from './HexBytes'; @jsonObject export class Proof { - @jsonMember({ name: 'public_key', constructor: PublicKey }) + @jsonMember(() => ({ constructor: PublicKey, name: 'public_key' })) public publicKey: PublicKey; - @jsonMember({ name: 'signature', constructor: HexBytes }) + @jsonMember(() => ({ name: 'signature', constructor: HexBytes })) public signature: HexBytes; } @jsonObject export class Block { - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember(() => ({ name: 'hash', constructor: Hash })) public hash: Hash; @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) public stateRootHash: Hash; - @jsonMember({ name: 'last_switch_block_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'last_switch_block_hash', constructor: Hash })) public lastSwitchBlockHash: Hash | null; - @jsonMember({ name: 'parent_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) public parentHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) public eraID: number; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) public timestamp: Timestamp; - @jsonMember({ name: 'accumulated_seed', constructor: Hash }) + @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) public accumulatedSeed?: Hash; @jsonMember({ name: 'random_bit', constructor: Boolean }) @@ -52,13 +52,13 @@ export class Block { @jsonMember({ name: 'current_gas_price', constructor: Number }) public currentGasPrice: number; - @jsonMember({ name: 'proposer', constructor: Proposer }) + @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) public proposer: Proposer; @jsonMember({ name: 'protocol_version', constructor: String }) public protocolVersion?: string; - @jsonMember({ name: 'era_end', constructor: EraEnd }) + @jsonMember(() => ({ name: 'era_end', constructor: EraEnd })) public eraEnd?: EraEnd; @jsonArrayMember(() => BlockTransaction, { name: 'transactions' }) @@ -67,10 +67,11 @@ export class Block { @jsonArrayMember(Number, { name: 'rewarded_signatures' }) public rewardedSignatures: number[]; - @jsonArrayMember(Proof, { name: 'proofs' }) + @jsonArrayMember(() => Proof, { name: 'proofs' }) public proofs: Proof[]; public originBlockV1?: BlockV1; + public originBlockV2?: BlockV2; constructor( @@ -202,13 +203,13 @@ export class Block { @jsonObject export class BlockTransaction { - @jsonMember({ name: 'category', constructor: Proof }) + @jsonMember({ name: 'category', constructor: Number }) public category: TransactionCategory; @jsonMember({ name: 'version', constructor: Number }) public version: TransactionVersion; - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember(() => ({ name: 'hash', constructor: Hash })) public hash: Hash; constructor( @@ -346,7 +347,7 @@ export function parseBlockTransactions(data: string): BlockTransaction[] { @jsonObject export class BlockV1 { - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember(() => ({ name: 'hash', constructor: Hash })) public hash: Hash; @jsonMember({ name: 'header', constructor: () => BlockHeaderV1 }) @@ -361,19 +362,19 @@ export class BlockV1 { @jsonObject export class BlockBodyV1 { - @jsonArrayMember(Hash, { name: 'deploy_hashes' }) + @jsonArrayMember(() => Hash, { name: 'deploy_hashes' }) public deployHashes: Hash[]; - @jsonMember({ name: 'proposer', constructor: Proposer }) + @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) public proposer: Proposer; - @jsonArrayMember(Hash, { name: 'transfer_hashes' }) + @jsonArrayMember(() => Hash, { name: 'transfer_hashes' }) public transferHashes: Hash[]; } @jsonObject export class BlockV2 { - @jsonMember({ name: 'hash', constructor: Hash }) + @jsonMember(() => ({ name: 'hash', constructor: Hash })) public hash: Hash; @jsonMember({ name: 'header', constructor: () => BlockHeaderV2 }) @@ -385,10 +386,10 @@ export class BlockV2 { @jsonObject export class BlockHeaderV1 { - @jsonMember({ name: 'accumulated_seed', constructor: Hash }) + @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) public accumulatedSeed?: Hash; - @jsonMember({ name: 'body_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'body_hash', constructor: Hash })) public bodyHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -397,7 +398,7 @@ export class BlockHeaderV1 { @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember({ name: 'parent_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) public parentHash: Hash; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -406,22 +407,22 @@ export class BlockHeaderV1 { @jsonMember({ name: 'random_bit', constructor: Boolean }) public randomBit: boolean; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) public stateRootHash: Hash; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) public timestamp: Timestamp; - @jsonMember({ name: 'era_end', constructor: EraEndV1 }) + @jsonMember(() => ({ name: 'era_end', constructor: EraEndV1 })) public eraEnd?: EraEndV1; } @jsonObject export class BlockHeaderV2 { - @jsonMember({ name: 'accumulated_seed', constructor: Hash }) + @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) public accumulatedSeed?: Hash; - @jsonMember({ name: 'body_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'body_hash', constructor: Hash })) public bodyHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -433,10 +434,10 @@ export class BlockHeaderV2 { @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember({ name: 'parent_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) public parentHash: Hash; - @jsonMember({ name: 'proposer', constructor: Proposer }) + @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) public proposer: Proposer; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -445,16 +446,16 @@ export class BlockHeaderV2 { @jsonMember({ name: 'random_bit', constructor: Boolean }) public randomBit: boolean; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) public stateRootHash: Hash; - @jsonMember({ name: 'last_switch_block_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'last_switch_block_hash', constructor: Hash })) public lastSwitchBlockHash: Hash; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) public timestamp: Timestamp; - @jsonMember({ name: 'era_end', constructor: EraEndV2 }) + @jsonMember(() => ({ name: 'era_end', constructor: EraEndV2 })) public eraEnd?: EraEndV2; } @@ -496,10 +497,10 @@ export class BlockHeaderWrapper { @jsonObject export class BlockHeader { - @jsonMember({ name: 'accumulated_seed', constructor: Hash }) + @jsonMember(() => ({ name: 'accumulated_seed', constructor: Hash })) public accumulatedSeed?: Hash; - @jsonMember({ name: 'body_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'body_hash', constructor: Hash })) public bodyHash: Hash; @jsonMember({ name: 'era_id', constructor: Number }) @@ -511,10 +512,10 @@ export class BlockHeader { @jsonMember({ name: 'height', constructor: Number }) public height: number; - @jsonMember({ name: 'parent_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'parent_hash', constructor: Hash })) public parentHash: Hash; - @jsonMember({ name: 'proposer', constructor: Proposer }) + @jsonMember(() => ({ name: 'proposer', constructor: Proposer })) public proposer: Proposer; @jsonMember({ name: 'protocol_version', constructor: String }) @@ -523,13 +524,13 @@ export class BlockHeader { @jsonMember({ name: 'random_bit', constructor: Boolean }) public randomBit: boolean; - @jsonMember({ name: 'state_root_hash', constructor: Hash }) + @jsonMember(() => ({ name: 'state_root_hash', constructor: Hash })) public stateRootHash: Hash; - @jsonMember({ name: 'timestamp', constructor: Timestamp }) + @jsonMember(() => ({ name: 'timestamp', constructor: Timestamp })) public timestamp: Timestamp; - @jsonMember({ name: 'era_end', constructor: EraEnd }) + @jsonMember(() => ({ name: 'era_end', constructor: EraEnd })) public eraEnd?: EraEnd | null; private originBlockHeaderV1?: BlockHeaderV1; diff --git a/src/types/Deploy.ts b/src/types/Deploy.ts index 8040c830d..b140e22f5 100644 --- a/src/types/Deploy.ts +++ b/src/types/Deploy.ts @@ -1,5 +1,4 @@ -import { jsonObject, jsonMember, jsonArrayMember, TypedJSON } from 'typedjson'; -import { blake2b } from '@noble/hashes/blake2b'; +import { jsonArrayMember, jsonMember, jsonObject, TypedJSON } from 'typedjson'; import { concat } from '@ethersproject/bytes'; import { BigNumber } from '@ethersproject/bignumber'; @@ -136,14 +135,36 @@ export class Deploy { @jsonMember({ constructor: ExecutableDeployItem }) public session: ExecutableDeployItem; + /** + * Constructs a `Deploy` object + * @param hash The DeployHash identifying this Deploy + * @param header The deploy header + * @param payment An ExecutableDeployItem representing the payment logic + * @param session An ExecutableDeployItem representing the session logic + * @param approvals An array of signatures and associated accounts who have approved this deploy + */ + constructor( + hash: Hash, + header: DeployHeader, + payment: ExecutableDeployItem, + session: ExecutableDeployItem, + approvals: Approval[] + ) { + this.approvals = approvals; + this.session = session; + this.payment = payment; + this.header = header; + this.hash = hash; + } + public validate(): boolean { const paymentBytes = this.payment.bytes(); const sessionBytes = this.session.bytes(); - const calculatedBodyHash = new Hash( - blake2b(new Uint8Array([...paymentBytes, ...sessionBytes])) - ); + const concatenatedBytes = concat([paymentBytes, sessionBytes]); + const calculatedBodyHash = new Hash(byteHash(concatenatedBytes)); + const headerBytes = this.header.toBytes(); - const calculatedHash = new Hash(blake2b(headerBytes)); + const calculatedHash = new Hash(byteHash(headerBytes)); if ( !this.header.bodyHash?.equals(calculatedBodyHash) || @@ -152,7 +173,7 @@ export class Deploy { throw new Error('Invalid deploy hash or body hash'); } - for (const approval of this.approvals) { + this.approvals.forEach(approval => { if ( !approval.signer.verifySignature( this.hash.toBytes(), @@ -161,7 +182,8 @@ export class Deploy { ) { throw new Error('Invalid approval signature'); } - } + }); + return true; } @@ -195,13 +217,7 @@ export class Deploy { session: ExecutableDeployItem, approvals: Approval[] = [] ): Deploy { - const deploy = new Deploy(); - deploy.hash = hash; - deploy.header = header; - deploy.payment = payment; - deploy.session = session; - deploy.approvals = approvals; - return deploy; + return new Deploy(hash, header, payment, session, approvals); } public static fromHeaderAndItems( @@ -211,9 +227,9 @@ export class Deploy { ): Deploy { const paymentBytes = payment.bytes(); const sessionBytes = session.bytes(); - const serializedBody = new Uint8Array([...paymentBytes, ...sessionBytes]); - deployHeader.bodyHash = new Hash(blake2b(serializedBody)); - const deployHash = new Hash(blake2b(deployHeader.toBytes())); + const serializedBody = concat([paymentBytes, sessionBytes]); + deployHeader.bodyHash = new Hash(byteHash(serializedBody)); + const deployHash = new Hash(byteHash(deployHeader.toBytes())); return Deploy.createNew(deployHash, deployHeader, payment, session); } @@ -328,7 +344,7 @@ export class Deploy { public static toJson = (deploy: Deploy) => { const serializer = new TypedJSON(Deploy); - return serializer.toPlainJson(deploy); + return { deploy: serializer.toPlainJson(deploy) }; }; } diff --git a/src/types/EntryPoint.ts b/src/types/EntryPoint.ts index 12c1875e5..a6adb4ee2 100644 --- a/src/types/EntryPoint.ts +++ b/src/types/EntryPoint.ts @@ -53,7 +53,7 @@ export class EntryPointV1 { @jsonMember({ name: 'entry_point_type', - constructor: () => EntryPointType + constructor: String }) entryPointType: EntryPointType; diff --git a/src/types/ExecutableDeployItem.ts b/src/types/ExecutableDeployItem.ts index 6c5200235..11611bad2 100644 --- a/src/types/ExecutableDeployItem.ts +++ b/src/types/ExecutableDeployItem.ts @@ -1,5 +1,6 @@ import { jsonMember, jsonObject } from 'typedjson'; import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; +import { concat } from '@ethersproject/bytes'; import { Args } from './Args'; import { @@ -50,11 +51,11 @@ export class ModuleBytes { moduleBytes ).bytes(); - const result = new Uint8Array([...lengthBytes, ...bytesArrayBytes]); + let result = concat([lengthBytes, bytesArrayBytes]); if (this.args) { const argBytes = this.args.toBytes(); - return new Uint8Array([...result, ...argBytes]); + result = concat([result, argBytes]); } return result; @@ -80,13 +81,11 @@ export class StoredContractByHash { } bytes(): Uint8Array { + const hashBytes = this.hash.hash.toBytes(); + const entryPointBytes = CLValueString.newCLString(this.entryPoint).bytes(); const argBytes = this.args.toBytes(); - return new Uint8Array([ - ...this.hash.hash.toBytes(), - ...CLValueString.newCLString(this.entryPoint).bytes(), - ...argBytes - ]); + return concat([hashBytes, entryPointBytes, argBytes]); } } @@ -109,12 +108,11 @@ export class StoredContractByName { } bytes(): Uint8Array { + const nameBytes = CLValueString.newCLString(this.name).bytes(); + const entryPointBytes = CLValueString.newCLString(this.entryPoint).bytes(); const argBytes = this.args.toBytes(); - return new Uint8Array([ - ...CLValueString.newCLString(this.name).bytes(), - ...CLValueString.newCLString(this.entryPoint).bytes(), - ...argBytes - ]); + + return concat([nameBytes, entryPointBytes, argBytes]); } } @@ -144,21 +142,16 @@ export class StoredVersionedContractByHash { } bytes(): Uint8Array { - const hash = this.hash.hash.toBytes(); - const option = new CLValueOption( + const hashBytes = this.hash.hash.toBytes(); + const optionBytes = new CLValueOption( this.version ? CLValueUInt32.newCLUInt32(BigNumber.from(this.version)) : null - ); - + ).bytes(); + const entryPointBytes = CLValueString.newCLString(this.entryPoint).bytes(); const argBytes = this.args?.toBytes() || new Uint8Array(); - return new Uint8Array([ - ...hash, - ...option.bytes(), - ...CLValueString.newCLString(this.entryPoint).bytes(), - ...argBytes - ]); + return concat([hashBytes, optionBytes, entryPointBytes, argBytes]); } } @@ -183,20 +176,16 @@ export class StoredVersionedContractByName { } bytes(): Uint8Array { - const option = new CLValueOption( + const nameBytes = CLValueString.newCLString(this.name).bytes(); + const optionBytes = new CLValueOption( this.version ? CLValueUInt32.newCLUInt32(BigNumber.from(this.version)) : null - ); - + ).bytes(); + const entryPointBytes = CLValueString.newCLString(this.entryPoint).bytes(); const argBytes = this.args?.toBytes() || new Uint8Array(); - return new Uint8Array([ - ...CLValueString.newCLString(this.name).bytes(), - ...option.bytes(), - ...CLValueString.newCLString(this.entryPoint).bytes(), - ...argBytes - ]); + return concat([nameBytes, optionBytes, entryPointBytes, argBytes]); } } @@ -289,37 +278,42 @@ export class ExecutableDeployItem { bytes(): Uint8Array { let bytes: Uint8Array; + if (this.moduleBytes) { bytes = this.moduleBytes.bytes(); - return new Uint8Array([ExecutableDeployItemType.ModuleBytes, ...bytes]); + return concat([ + Uint8Array.of(ExecutableDeployItemType.ModuleBytes), + bytes + ]); } else if (this.storedContractByHash) { bytes = this.storedContractByHash.bytes(); - return new Uint8Array([ - ExecutableDeployItemType.StoredContractByHash, - ...bytes + return concat([ + Uint8Array.of(ExecutableDeployItemType.StoredContractByHash), + bytes ]); } else if (this.storedContractByName) { bytes = this.storedContractByName.bytes(); - return new Uint8Array([ - ExecutableDeployItemType.StoredContractByName, - ...bytes + return concat([ + Uint8Array.of(ExecutableDeployItemType.StoredContractByName), + bytes ]); } else if (this.storedVersionedContractByHash) { bytes = this.storedVersionedContractByHash.bytes(); - return new Uint8Array([ - ExecutableDeployItemType.StoredVersionedContractByHash, - ...bytes + return concat([ + Uint8Array.of(ExecutableDeployItemType.StoredVersionedContractByHash), + bytes ]); } else if (this.storedVersionedContractByName) { bytes = this.storedVersionedContractByName.bytes(); - return new Uint8Array([ - ExecutableDeployItemType.StoredVersionedContractByName, - ...bytes + return concat([ + Uint8Array.of(ExecutableDeployItemType.StoredVersionedContractByName), + bytes ]); } else if (this.transfer) { bytes = this.transfer.bytes(); - return new Uint8Array([ExecutableDeployItemType.Transfer, ...bytes]); + return concat([Uint8Array.of(ExecutableDeployItemType.Transfer), bytes]); } + return new Uint8Array(); } diff --git a/src/types/InitiatorAddr.ts b/src/types/InitiatorAddr.ts index 04f55525f..dbe6ee3a4 100644 --- a/src/types/InitiatorAddr.ts +++ b/src/types/InitiatorAddr.ts @@ -1,4 +1,6 @@ import { jsonObject, jsonMember } from 'typedjson'; +import { concat } from '@ethersproject/bytes'; + import { PublicKey } from './keypair'; import { AccountHash } from './key'; @@ -16,17 +18,20 @@ export class InitiatorAddr { } public toBytes(): Uint8Array { - const result: number[] = []; + let result: Uint8Array; if (this.accountHash) { - result.push(1); - result.push(...this.accountHash.toBytes()); + const prefix = new Uint8Array([1]); + result = concat([prefix, this.accountHash.toBytes()]); } else if (this.publicKey) { - result.push(0); - result.push(...(this.publicKey.bytes() ?? [])); + const prefix = new Uint8Array([0]); + const publicKeyBytes = this.publicKey.bytes() || new Uint8Array(0); + result = concat([prefix, publicKeyBytes]); + } else { + result = new Uint8Array(0); } - return new Uint8Array(result); + return result; } static fromJSON(json: string): InitiatorAddr { diff --git a/src/types/PricingMode.ts b/src/types/PricingMode.ts index 94a0974a4..d509e82c4 100644 --- a/src/types/PricingMode.ts +++ b/src/types/PricingMode.ts @@ -1,3 +1,5 @@ +import { concat } from '@ethersproject/bytes'; + import { jsonObject, jsonMember } from 'typedjson'; import { Hash } from './key'; import { CLValueUInt64 } from './clvalue'; @@ -44,21 +46,40 @@ export class PricingMode { reserved?: ReservedMode; toBytes(): Uint8Array { - const result: number[] = []; + let result: Uint8Array; + if (this.classic) { - result.push(PricingModeTag.Classic); - result.push( - ...new CLValueUInt64(BigInt(this.classic.paymentAmount)).bytes() - ); - result.push(this.classic.gasPriceTolerance); - result.push(this.classic.standardPayment ? 1 : 0); + const classicPaymentBytes = new CLValueUInt64( + BigInt(this.classic.paymentAmount) + ).bytes(); + const gasPriceToleranceByte = new Uint8Array([ + this.classic.gasPriceTolerance + ]); + const standardPaymentByte = new Uint8Array([ + this.classic.standardPayment ? 1 : 0 + ]); + + result = concat([ + Uint8Array.of(PricingModeTag.Classic), + classicPaymentBytes, + gasPriceToleranceByte, + standardPaymentByte + ]); } else if (this.fixed) { - result.push(PricingModeTag.Fixed); - result.push(this.fixed.gasPriceTolerance); + const gasPriceToleranceByte = new Uint8Array([ + this.fixed.gasPriceTolerance + ]); + result = concat([ + Uint8Array.of(PricingModeTag.Fixed), + gasPriceToleranceByte + ]); } else if (this.reserved) { - result.push(PricingModeTag.Reserved); - result.push(...this.reserved.receipt.toBytes()); + const receiptBytes = this.reserved.receipt.toBytes(); + result = concat([Uint8Array.of(PricingModeTag.Reserved), receiptBytes]); + } else { + result = new Uint8Array(0); // empty array if none of the conditions match } - return new Uint8Array(result); + + return result; } } diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index 144ed34fd..a12683dd9 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -1,5 +1,5 @@ import { jsonObject, jsonMember, jsonArrayMember, TypedJSON } from 'typedjson'; -import { blake2b } from '@noble/hashes/blake2b'; +import { concat } from '@ethersproject/bytes'; import { Hash } from './key'; import { Deploy } from './Deploy'; @@ -15,6 +15,7 @@ import { PrivateKey } from './keypair/PrivateKey'; import { CLValueString, CLValueUInt64 } from './clvalue'; import { Args } from './Args'; import { deserializeArgs, serializeArgs } from './SerializationUtils'; +import { byteHash } from './ByteConverters'; export class TransactionError extends Error {} export const ErrInvalidBodyHash = new TransactionError('invalid body hash'); @@ -100,29 +101,26 @@ export class TransactionV1Header { } public toBytes(): Uint8Array { - const result = []; - const chainNameBytes = CLValueString.newCLString(this.chainName).bytes(); - result.push(...chainNameBytes); - const timestampMillis = this.timestamp.toMilliseconds(); const timestampBytes = CLValueUInt64.newCLUint64( BigInt(timestampMillis) ).bytes(); - result.push(...timestampBytes); - const ttlBytes = CLValueUInt64.newCLUint64( BigInt(this.ttl.toMilliseconds()) ).bytes(); - result.push(...ttlBytes); - - result.push(...this.bodyHash.toBytes()); - - result.push(...this.pricingMode.toBytes()); - - result.push(...this.initiatorAddr.toBytes()); - - return new Uint8Array(result); + const bodyHashBytes = this.bodyHash.toBytes(); + const pricingModeBytes = this.pricingMode.toBytes(); + const initiatorAddrBytes = this.initiatorAddr.toBytes(); + + return concat([ + chainNameBytes, + timestampBytes, + ttlBytes, + bodyHashBytes, + pricingModeBytes, + initiatorAddrBytes + ]); } } @@ -171,23 +169,19 @@ export class TransactionV1Body { } toBytes(): Uint8Array { - const result = []; - - const argsBytes = this.args?.toBytes(); - result.push(...argsBytes); - + const argsBytes = this.args?.toBytes() || new Uint8Array(); const targetBytes = this.target.toBytes(); - result.push(...targetBytes); - const entryPointBytes = this.entryPoint.bytes(); - result.push(...entryPointBytes); - - result.push(this.category); - + const categoryBytes = new Uint8Array([this.category]); const schedulingBytes = this.scheduling.bytes(); - result.push(...schedulingBytes); - return new Uint8Array(result); + return concat([ + argsBytes, + targetBytes, + entryPointBytes, + categoryBytes, + schedulingBytes + ]); } } @@ -220,12 +214,12 @@ export class TransactionV1 { public validate(): void { const bodyBytes = this.body.toBytes(); - if (!this.arrayEquals(blake2b(bodyBytes), this.header.bodyHash.toBytes())) + if (!this.arrayEquals(byteHash(bodyBytes), this.header.bodyHash.toBytes())) throw ErrInvalidBodyHash; const headerBytes = this.header.toBytes(); - if (!this.arrayEquals(blake2b(headerBytes), this.hash.toBytes())) + if (!this.arrayEquals(byteHash(headerBytes), this.hash.toBytes())) throw ErrInvalidTransactionHash; for (const approval of this.approvals) { @@ -294,14 +288,10 @@ export class TransactionV1 { transactionBody: TransactionV1Body ): TransactionV1 { const bodyBytes = transactionBody.toBytes(); - transactionHeader.bodyHash = new Hash( - new Uint8Array(blake2b(bodyBytes, { dkLen: 32 })) - ); + transactionHeader.bodyHash = new Hash(new Uint8Array(byteHash(bodyBytes))); const headerBytes = transactionHeader.toBytes(); - const transactionHash = new Hash( - new Uint8Array(blake2b(headerBytes, { dkLen: 32 })) - ); + const transactionHash = new Hash(new Uint8Array(byteHash(headerBytes))); return new TransactionV1( transactionHash, transactionHeader, @@ -353,7 +343,7 @@ export class TransactionV1 { public static toJson = (transaction: TransactionV1) => { const serializer = new TypedJSON(TransactionV1); - return serializer.toPlainJson(transaction); + return { transaction: serializer.toPlainJson(transaction) }; }; } diff --git a/src/types/TransactionEntryPoint.ts b/src/types/TransactionEntryPoint.ts index ca5f935b7..5d459e9b1 100644 --- a/src/types/TransactionEntryPoint.ts +++ b/src/types/TransactionEntryPoint.ts @@ -1,4 +1,6 @@ import { jsonObject, jsonMember } from 'typedjson'; +import { concat } from '@ethersproject/bytes'; + import { CLValueString } from './clvalue'; export enum TransactionEntryPointEnum { @@ -98,12 +100,11 @@ export class TransactionEntryPoint { return TransactionEntryPointTag.Custom; } - // Convert entry point to bytes, adding custom entry data if present bytes(): Uint8Array { let result = new Uint8Array([this.tag()]); if (this.custom) { const customBytes = new CLValueString(this.custom).bytes(); - result = new Uint8Array([...result, ...customBytes]); + result = concat([result, customBytes]); } return result; } diff --git a/src/types/TransactionScheduling.ts b/src/types/TransactionScheduling.ts index 3964ea1b7..f1d544121 100644 --- a/src/types/TransactionScheduling.ts +++ b/src/types/TransactionScheduling.ts @@ -1,4 +1,6 @@ import { jsonObject, jsonMember } from 'typedjson'; +import { concat } from '@ethersproject/bytes'; + import { Timestamp } from './Time'; import { CLValueUInt64 } from './clvalue'; @@ -60,18 +62,19 @@ export class TransactionScheduling { } bytes(): Uint8Array { - const result = [this.tag()]; + const tagBytes = Uint8Array.of(this.tag()); if (this.futureEra) { - result.push(...new CLValueUInt64(BigInt(this.futureEra.eraID)).bytes()); + const eraBytes = new CLValueUInt64(BigInt(this.futureEra.eraID)).bytes(); + return concat([tagBytes, eraBytes]); } else if (this.futureTimestamp) { const timestampBytes = new CLValueUInt64( BigInt(this.futureTimestamp.timestamp.toMilliseconds()) ).bytes(); - result.push(...timestampBytes); + return concat([tagBytes, timestampBytes]); } - return new Uint8Array(result); + return tagBytes; } static fromJSON(json: string): TransactionScheduling { diff --git a/src/types/TransactionTarget.ts b/src/types/TransactionTarget.ts index 50cbf7956..54e3ea1ff 100644 --- a/src/types/TransactionTarget.ts +++ b/src/types/TransactionTarget.ts @@ -1,4 +1,5 @@ import isNull from 'lodash/isNull'; +import { concat } from '@ethersproject/bytes'; import { jsonMember, jsonObject } from 'typedjson'; import { getRuntimeTag, TransactionRuntime } from './AddressableEntity'; @@ -106,55 +107,77 @@ export class TransactionTarget { } toBytes(): Uint8Array { - const result: number[] = []; + let result: Uint8Array = new Uint8Array(); if (this.native !== undefined) { - result.push(TransactionTargetType.Native); + result = concat([result, Uint8Array.of(TransactionTargetType.Native)]); } else if (this.stored !== undefined) { - result.push(TransactionTargetType.Stored); + result = concat([result, Uint8Array.of(TransactionTargetType.Stored)]); + if (this.stored.id.byHash !== undefined) { - result.push(InvocationTargetTag.ByHash); - result.push(...this.stored.id.byHash.toBytes()); + result = concat([ + result, + Uint8Array.of(InvocationTargetTag.ByHash), + this.stored.id.byHash.toBytes() + ]); } else if (this.stored.id.byName !== undefined) { - result.push(InvocationTargetTag.ByName); - result.push(...new CLValueString(this.stored.id.byName).bytes()); + const nameBytes = new CLValueString(this.stored.id.byName).bytes(); + result = concat([ + result, + Uint8Array.of(InvocationTargetTag.ByName), + nameBytes + ]); } else if (this.stored.id.byPackageHash !== undefined) { - result.push(InvocationTargetTag.ByPackageHash); - result.push(...this.stored.id.byPackageHash.addr.toBytes()); + result = concat([ + result, + Uint8Array.of(InvocationTargetTag.ByPackageHash), + this.stored.id.byPackageHash.addr.toBytes() + ]); + if (this.stored.id.byPackageHash.version !== undefined) { - result.push(1); - result.push( - ...this.uint32ToBytes(this.stored.id.byPackageHash.version) + const versionBytes = this.uint32ToBytes( + this.stored.id.byPackageHash.version ); + result = concat([result, Uint8Array.of(1), versionBytes]); } else { - result.push(0); + result = concat([result, Uint8Array.of(0)]); } } else if (this.stored.id.byPackageName !== undefined) { - result.push(InvocationTargetTag.ByPackageName); - result.push( - ...new CLValueString(this.stored.id.byPackageName.name).bytes() - ); + const nameBytes = new CLValueString( + this.stored.id.byPackageName.name + ).bytes(); + result = concat([ + result, + Uint8Array.of(InvocationTargetTag.ByPackageName), + nameBytes + ]); + if (this.stored.id.byPackageName.version !== undefined) { - result.push(1); - result.push( - ...this.uint32ToBytes(this.stored.id.byPackageName.version) + const versionBytes = this.uint32ToBytes( + this.stored.id.byPackageName.version ); + result = concat([result, Uint8Array.of(1), versionBytes]); } else { - result.push(0); + result = concat([result, Uint8Array.of(0)]); } } - result.push(getRuntimeTag(this.stored.runtime)); + + const runtimeTag = getRuntimeTag(this.stored.runtime); + result = concat([result, Uint8Array.of(runtimeTag)]); } else if (this.session !== undefined) { - result.push(TransactionTargetType.Session); + result = concat([result, Uint8Array.of(TransactionTargetType.Session)]); const moduleBytes = this.session.moduleBytes ? this.hexStringToBytes(this.session.moduleBytes) : new Uint8Array([0]); - result.push(...this.uint32ToBytes(moduleBytes.length), ...moduleBytes); - result.push(getRuntimeTag(this.session.runtime)); + const moduleLengthBytes = this.uint32ToBytes(moduleBytes.length); + result = concat([result, moduleLengthBytes, moduleBytes]); + + const runtimeTag = getRuntimeTag(this.session.runtime); + result = concat([result, Uint8Array.of(runtimeTag)]); } - return new Uint8Array(result); + return result; } static fromJSON(json: string): TransactionTarget { diff --git a/src/types/clvalue/Map.ts b/src/types/clvalue/Map.ts index 0591f4f56..00e991ff2 100644 --- a/src/types/clvalue/Map.ts +++ b/src/types/clvalue/Map.ts @@ -61,9 +61,9 @@ export class CLValueMap { */ public getMap(): Record { const result: Record = {}; - for (const [k, v] of this.indexedData.entries()) { + this.indexedData.forEach((v, k) => { result[k] = v; - } + }); return result; } @@ -81,9 +81,9 @@ export class CLValueMap { */ public toString(): string { const b: string[] = []; - for (const [key, value] of this.indexedData.entries()) { + this.indexedData.forEach((value, key) => { b.push(`(${key}="${value.toString()}")`); - } + }); return b.join(''); } diff --git a/src/types/clvalue/String.ts b/src/types/clvalue/String.ts index ab5216d0b..c476d2780 100644 --- a/src/types/clvalue/String.ts +++ b/src/types/clvalue/String.ts @@ -1,3 +1,5 @@ +import { concat } from '@ethersproject/bytes'; + import { CLTypeString } from './cltype'; import { CLValue, IResultWithBytes } from './CLValue'; import { CLValueUInt32 } from './Uint32'; @@ -24,7 +26,7 @@ export class CLValueString { public bytes(): Uint8Array { const sizeBytes = this.sizeToBytes(this.value.length); const valueBytes = new TextEncoder().encode(this.value); - return new Uint8Array([...sizeBytes, ...valueBytes]); + return concat([sizeBytes, valueBytes]); } private sizeToBytes(size: number): Uint8Array { diff --git a/src/types/key/ByteCode.ts b/src/types/key/ByteCode.ts index 143eddda1..e504e9472 100644 --- a/src/types/key/ByteCode.ts +++ b/src/types/key/ByteCode.ts @@ -1,3 +1,5 @@ +import { concat } from '@ethersproject/bytes'; + import { Hash } from './Hash'; import { PrefixName } from './Key'; import { IResultWithBytes } from '../clvalue'; @@ -139,12 +141,12 @@ export class ByteCode { */ toBytes(): Uint8Array { if (this.V1CasperWasm) { - return Uint8Array.of( - ByteCodeKind.V1CasperWasmKind, - ...this.V1CasperWasm.toBytes() - ); + const kindBytes = new Uint8Array([ByteCodeKind.V1CasperWasmKind]); + const wasmBytes = this.V1CasperWasm.toBytes(); + + return concat([kindBytes, wasmBytes]); } else if (this.isEmpty) { - return Uint8Array.of(ByteCodeKind.EmptyKind); + return new Uint8Array([ByteCodeKind.EmptyKind]); } else { throw new Error('Unexpected ByteCode type'); } diff --git a/src/types/key/EntityAddr.ts b/src/types/key/EntityAddr.ts index 098d56640..f4dd6c683 100644 --- a/src/types/key/EntityAddr.ts +++ b/src/types/key/EntityAddr.ts @@ -1,4 +1,6 @@ import { jsonObject, jsonMember } from 'typedjson'; +import { concat } from '@ethersproject/bytes'; + import { Hash } from './Hash'; import { IResultWithBytes } from '../clvalue'; @@ -109,7 +111,7 @@ export class EntityAddr { throw new Error('Unexpected EntityAddr type'); } - return new Uint8Array([prefix, ...bytes]); + return concat([Uint8Array.from([prefix]), bytes]); } /** diff --git a/src/types/key/EntryPointAddr.ts b/src/types/key/EntryPointAddr.ts index dac7b9be9..0ba0b0422 100644 --- a/src/types/key/EntryPointAddr.ts +++ b/src/types/key/EntryPointAddr.ts @@ -1,4 +1,6 @@ import { jsonArrayMember, jsonMember, jsonObject } from 'typedjson'; +import { concat } from '@ethersproject/bytes'; + import { EntityAddr } from './EntityAddr'; import { Hash } from './Hash'; import { Conversions } from '../Conversions'; @@ -163,21 +165,28 @@ export class EntryPointAddr { * @throws EntryPointError if the EntryPointAddr type is unexpected. */ toBytes(): Uint8Array { - const result = []; + let result: Uint8Array; + if (this.vmCasperV1) { - result.push(EntryPointTag.V1EntryPoint); - result.push(...this.vmCasperV1.entityAddr.toBytes()); - result.push(...this.vmCasperV1.nameBytes); + const entryPointTag = new Uint8Array([EntryPointTag.V1EntryPoint]); + const entityBytes = this.vmCasperV1.entityAddr.toBytes(); + const nameBytes = this.vmCasperV1.nameBytes; + + result = concat([entryPointTag, entityBytes, nameBytes]); } else if (this.vmCasperV2) { - result.push(EntryPointTag.V2EntryPoint); - result.push(...this.vmCasperV2.entityAddr.toBytes()); + const entryPointTag = new Uint8Array([EntryPointTag.V2EntryPoint]); + const entityBytes = this.vmCasperV2.entityAddr.toBytes(); + const selectorBuffer = Buffer.alloc(SelectorBytesLen); selectorBuffer.writeUInt32LE(this.vmCasperV2.selector, 0); - result.push(...selectorBuffer); + const selectorBytes = new Uint8Array(selectorBuffer); + + result = concat([entryPointTag, entityBytes, selectorBytes]); } else { throw new EntryPointError('Unexpected EntryPointAddr type'); } - return new Uint8Array(result); + + return result; } /** diff --git a/src/types/key/Key.ts b/src/types/key/Key.ts index f188d15b1..2e565c1b2 100644 --- a/src/types/key/Key.ts +++ b/src/types/key/Key.ts @@ -561,30 +561,33 @@ export class Key { source: string, prefixes: Map ): PrefixName | undefined { - for (const [prefix] of prefixes) { + let result: PrefixName | undefined = undefined; + + prefixes.forEach((_, prefix) => { if (source.startsWith(prefix)) { if ( prefix === PrefixName.EraId && source.startsWith(PrefixName.EraSummary) ) { - return PrefixName.EraSummary; - } - if ( + result = PrefixName.EraSummary; + } else if ( prefix === PrefixName.Bid && source.startsWith(PrefixName.BidAddr) ) { - return PrefixName.BidAddr; - } - if ( + result = PrefixName.BidAddr; + } else if ( prefix === PrefixName.Balance && source.startsWith(PrefixName.BalanceHold) ) { - return PrefixName.BalanceHold; + result = PrefixName.BalanceHold; + } else { + result = prefix; } - return prefix; + return; // Exit early from forEach if a match is found } - } - return undefined; + }); + + return result; } /** diff --git a/src/types/key/URef.ts b/src/types/key/URef.ts index 13adc914d..60e113aa6 100644 --- a/src/types/key/URef.ts +++ b/src/types/key/URef.ts @@ -1,6 +1,7 @@ import { jsonObject, jsonMember, jsonArrayMember } from 'typedjson'; import { IResultWithBytes } from '../clvalue'; import { Conversions } from '../Conversions'; +import { concat } from '@ethersproject/bytes'; /** * Enum representing the access permissions of a URef. @@ -62,7 +63,8 @@ export class URef { * @returns A Uint8Array representing the URef. */ bytes(): Uint8Array { - return Uint8Array.from([...this.data, this.access]); + const accessBytes = new Uint8Array([this.access]); + return concat([this.data, accessBytes]); } /** diff --git a/src/types/keypair/PrivateKey.ts b/src/types/keypair/PrivateKey.ts index b9c990a17..a9f95e7ff 100644 --- a/src/types/keypair/PrivateKey.ts +++ b/src/types/keypair/PrivateKey.ts @@ -1,3 +1,5 @@ +import { concat } from '@ethersproject/bytes'; + import { PublicKey } from './PublicKey'; import { PrivateKey as Ed25519PrivateKey } from './ed25519/PrivateKey'; import { PrivateKey as Secp256k1PrivateKey } from './secp256k1/PrivateKey'; @@ -77,7 +79,8 @@ export class PrivateKey { */ public async sign(msg: Uint8Array): Promise { const signature = await this.priv.sign(msg); - return new Uint8Array([this.alg, ...signature]); + const algBytes = Uint8Array.of(this.alg); + return concat([algBytes, signature]); } /** @@ -97,7 +100,8 @@ export class PrivateKey { public static async generate(algorithm: KeyAlgorithm): Promise { const priv = await PrivateKeyFactory.createPrivateKey(algorithm); const pubBytes = await priv.publicKeyBytes(); - const pub = PublicKey.fromBuffer(new Uint8Array([algorithm, ...pubBytes])); + const algBytes = Uint8Array.of(algorithm); + const pub = PublicKey.fromBuffer(concat([algBytes, pubBytes])); return new PrivateKey(algorithm, pub, priv); } @@ -116,7 +120,8 @@ export class PrivateKey { algorithm ); const pubBytes = await priv.publicKeyBytes(); - const pub = PublicKey.fromBuffer(new Uint8Array([algorithm, ...pubBytes])); + const algBytes = Uint8Array.of(algorithm); + const pub = PublicKey.fromBuffer(concat([algBytes, pubBytes])); return new PrivateKey(algorithm, pub, priv); } @@ -135,7 +140,8 @@ export class PrivateKey { algorithm ); const pubBytes = await priv.publicKeyBytes(); - const pub = PublicKey.fromBuffer(new Uint8Array([algorithm, ...pubBytes])); + const algBytes = Uint8Array.of(algorithm); + const pub = PublicKey.fromBuffer(concat([algBytes, pubBytes])); return new PrivateKey(algorithm, pub, priv); } } diff --git a/src/types/keypair/PublicKey.ts b/src/types/keypair/PublicKey.ts index 597530f63..01a4679af 100644 --- a/src/types/keypair/PublicKey.ts +++ b/src/types/keypair/PublicKey.ts @@ -1,12 +1,13 @@ import { jsonObject, jsonMember } from 'typedjson'; -import { blake2b } from '@noble/hashes/blake2b'; import { Buffer } from 'buffer'; +import { concat } from '@ethersproject/bytes'; import { PublicKey as Ed25519PublicKey } from './ed25519/PublicKey'; import { PublicKey as Secp256k1PublicKey } from './secp256k1/PublicKey'; import { Hash, AccountHash } from '../key'; import { Conversions } from '../Conversions'; import { IResultWithBytes } from '../clvalue'; +import { byteHash } from '../ByteConverters'; /** Error thrown when the signature is empty. */ const ErrEmptySignature = new Error('empty signature'); @@ -51,7 +52,7 @@ interface PublicKeyInternal { @jsonObject export class PublicKey { /** The cryptographic algorithm used for the key. */ - @jsonMember({ constructor: String }) + @jsonMember({ constructor: Number }) cryptoAlg: KeyAlgorithm; /** The key data associated with the public key. */ @@ -75,7 +76,11 @@ export class PublicKey { if (!this.key) { return new Uint8Array(); } - return new Uint8Array([this.cryptoAlg, ...this.key.bytes()]); + + const cryptoAlgBytes = new Uint8Array([this.cryptoAlg]); + const keyBytes = this.key.bytes(); + + return concat([cryptoAlgBytes, keyBytes]); } /** @@ -158,13 +163,13 @@ export class PublicKey { } const algString = KeyAlgorithm[this.cryptoAlg].toLowerCase(); - const bytesToHash = new Uint8Array([ - ...new TextEncoder().encode(algString), - 0, - ...this.key.bytes() - ]); + const algBytes = new TextEncoder().encode(algString); + const separatorByte = new Uint8Array([0]); + const keyBytes = this.key.bytes(); + + const bytesToHash = concat([algBytes, separatorByte, keyBytes]); - const blakeHash = blake2b(bytesToHash, { dkLen: 32 }); + const blakeHash = byteHash(bytesToHash); const hash = Hash.fromBuffer(Buffer.from(blakeHash)); return new AccountHash(hash, 'account-hash'); } diff --git a/tsconfig.base.json b/tsconfig.base.json index b5b01c05c..43785d24b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,7 +16,7 @@ "esModuleInterop": true, "resolveJsonModule": true, "module": "commonjs", - "target": "es2015", + "target": "es5", "lib": ["es2015", "dom"], "skipLibCheck": true, "typeRoots": ["./src/@types", "./node_modules/@types"]