Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ethereumjs/ethash

Package Overview
Dependencies
Maintainers
4
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ethereumjs/ethash - npm Package Compare versions

Comparing version 2.1.0 to 3.0.0-rc.1

dist/cjs/index.d.ts

36

package.json
{
"name": "@ethereumjs/ethash",
"version": "2.1.0",
"description": "An ethash implementation in JavaScript",
"version": "3.0.0-rc.1",
"description": "An implementation of the Ethash consensus algorithm in JavaScript",
"keywords": [

@@ -20,4 +20,11 @@ "ethash",

"author": "mjbecze <mjbecze@gmail.com>",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
},
"files": [

@@ -30,4 +37,5 @@ "dist",

"clean": "../../config/cli/clean-package.sh",
"coverage": "../../config/cli/coverage.sh",
"docs:build": "typedoc --options typedoc.js",
"coverage": "npx vitest run --coverage.enabled --coverage.reporter=lcov",
"docs:build": "typedoc --options typedoc.cjs",
"examples": "ts-node ../../scripts/examples-runner.ts -- ethash",
"lint": "../../config/cli/lint.sh",

@@ -37,20 +45,18 @@ "lint:diff": "../../config/cli/lint-diff.sh",

"prepublishOnly": "../../config/cli/prepublish.sh",
"test": "tape -r ts-node/register test/*.spec.ts",
"test": "npx vitest run",
"tsc": "../../config/cli/ts-compile.sh"
},
"dependencies": {
"@ethereumjs/block": "^4.3.0",
"@ethereumjs/rlp": "^4.0.1",
"@ethereumjs/util": "^8.1.0",
"abstract-level": "^1.0.3",
"@ethereumjs/block": "5.0.0-rc.1",
"@ethereumjs/rlp": "5.0.0-rc.1",
"@ethereumjs/util": "9.0.0-rc.1",
"bigint-crypto-utils": "^3.2.2",
"ethereum-cryptography": "^2.0.0"
"ethereum-cryptography": "^2.1.2"
},
"devDependencies": {
"@ethereumjs/common": "^3.2.0",
"memory-level": "^1.0.0"
"@ethereumjs/common": "4.0.0-rc.1"
},
"engines": {
"node": ">=14"
"node": ">=18"
}
}

@@ -9,2 +9,4 @@ # @ethereumjs/ethash

Note: this README has been updated containing the changes from our next breaking release round [UNRELEASED] targeted for Summer 2023. See the README files from the [maintenance-v6](https://github.com/ethereumjs/ethereumjs-monorepo/tree/maintenance-v6/) branch for documentation matching our latest releases.
| [Ethash](https://github.com/ethereum/wiki/wiki/Ethash) implementation in TypeScript. |

@@ -30,11 +32,11 @@ | ------------------------------------------------------------------------------------ |

import { Block } from '@ethereumjs/block'
import { MemoryLevel } from 'memory-level'
import { hexToBytes, MapDB } from '@ethereumjs/util'
const cacheDB = level()
const cacheDb = new MapDB<number, DBObject>()
const ethash = new Ethash(cacheDB)
const validblockRlp =
'f90667f905fba0a8d5b7a4793baaede98b5236954f634a0051842df6a252f6a80492fd888678bda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0f93c8db1e931daa2e22e39b5d2da6fb4074e3d544094857608536155e3521bc1a0bb7495628f9160ddbcf6354380ee32c300d594e833caec3a428041a66e7bade1a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b84559c17b9b9040001020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000a09c7b47112a3afb385c12924bf6280d273c106eea7caeaf5131d8776f61056c148876ae05d46b58d1fff866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba01d2c92cfaeb04e53acdff2b5d42005ff6aacdb0105e64eb8c30c273f445d2782a01e7d50ffce57840360c57d94977b8cdebde614da23e8d1e77dc07928763cfe21c0'
'0xf90667f905fba0a8d5b7a4793baaede98b5236954f634a0051842df6a252f6a80492fd888678bda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0f93c8db1e931daa2e22e39b5d2da6fb4074e3d544094857608536155e3521bc1a0bb7495628f9160ddbcf6354380ee32c300d594e833caec3a428041a66e7bade1a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefd882560b84559c17b9b9040001020304050607080910111213141516171819202122232410000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000100000000000000000002000000000000000000030000000000000000000400000000000000000005000000000000000000060000000000000000000700000000000000000008000000000000000000090000000000000000000100000000000000000001000000000000000000020000000000000000000300000000000000000004000000000000000000050000000000000000000600000000000000000007000000000000000000080000000000000000000900000000000000000001000000000000000000010000000000000000000200000000000000000003000000000000000000040000000000000000000500000000000000000006000000000000000000070000000000000000000800000000000000000009000000000000000000010000000000000000000a09c7b47112a3afb385c12924bf6280d273c106eea7caeaf5131d8776f61056c148876ae05d46b58d1fff866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ba01d2c92cfaeb04e53acdff2b5d42005ff6aacdb0105e64eb8c30c273f445d2782a01e7d50ffce57840360c57d94977b8cdebde614da23e8d1e77dc07928763cfe21c0'
const validBlock = Block.fromRLPSerializedBlock(Buffer.from(validblockRlp, 'hex'))
const validBlock = Block.fromRLPSerializedBlock(hexToBytes(validblockRlp))

@@ -55,6 +57,4 @@ const result = await ethash.verifyPOW(validBlock)

import { Common } from '@ethereumjs/common'
import { BN } from 'ethereumjs-util'
import { MemoryLevel } from 'memory-level'
import { MapDB } from 'ethereumjs-util'
const cacheDB = new MemoryLevel()
const block = Block.fromBlockData({

@@ -67,2 +67,4 @@ header: {

const cacheDb = new MapDB<number, DBObject>()
const e = new Ethash(cacheDB)

@@ -79,2 +81,26 @@ const miner = e.getMiner(block.header)

### Hybrid CJS/ESM Builds
With the breaking releases from Summer 2023 we have started to ship our libraries with both CommonJS (`cjs` folder) and ESM builds (`esm` folder), see `package.json` for the detailed setup.
If you use an ES6-style `import` in your code files from the ESM build will be used:
```typescript
import { EthereumJSClass } from '@ethereumjs/[PACKAGE_NAME]'
```
If you use Node.js specific `require` the CJS build will be used:
```typescript
const { EthereumJSClass } = require('@ethereumjs/[PACKAGE_NAME]')
```
Using ESM will give you additional advantages over CJS beyond browser usage like static code analysis / Tree Shaking which CJS can not provide.
### Buffer -> Uint8Array
With the breaking releases from Summer 2023 we have removed all Node.js specific `Buffer` usages from our libraries and replace these with [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) representations, which are available both in Node.js and the browser (`Buffer` is a subclass of `Uint8Array`).
We have converted existing Buffer conversion methods to Uint8Array conversion methods in the [@ethereumjs/util](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/util) `bytes` module, see the respective README section for guidance.
### BigInt Support

@@ -81,0 +107,0 @@

import { Block, BlockHeader } from '@ethereumjs/block'
import { RLP } from '@ethereumjs/rlp'
import {
KeyEncoding,
TWO_POW256,
bigIntToBuffer,
bufArrToArr,
bufferToBigInt,
ValueEncoding,
bigIntToBytes,
bytesToBigInt,
bytesToHex,
concatBytes,
equalsBytes,
hexToBytes,
setLengthLeft,
zeros,
} from '@ethereumjs/util'
import { keccak256, keccak512 } from 'ethereum-cryptography/keccak'
import { keccak256, keccak512 } from 'ethereum-cryptography/keccak.js'
import {
bufReverse,
bytesReverse,
fnv,
fnvBuffer,
fnvBytes,
getCacheSize,

@@ -22,10 +27,10 @@ getEpoc,

params,
} from './util'
} from './util.js'
import type { BlockData, HeaderData } from '@ethereumjs/block'
import type { AbstractLevel } from 'abstract-level'
import type { DB, DBObject } from '@ethereumjs/util'
function xor(a: Buffer, b: Buffer) {
function xor(a: Uint8Array, b: Uint8Array) {
const len = Math.max(a.length, b.length)
const res = Buffer.alloc(len)
const res = new Uint8Array(len)
for (let i = 0; i < len; i++) {

@@ -38,4 +43,4 @@ res[i] = a[i] ^ b[i]

export type Solution = {
mixHash: Buffer
nonce: Buffer
mixHash: Uint8Array
nonce: Uint8Array
}

@@ -51,3 +56,3 @@

private currentNonce: bigint
private headerHash?: Buffer
private headerHash?: Uint8Array
private stopMining: boolean

@@ -96,3 +101,3 @@

data.header!.nonce = solution.nonce
return Block.fromBlockData(data, { common: this.block._common })
return Block.fromBlockData(data, { common: this.block.common })
} else {

@@ -102,3 +107,3 @@ const data = <HeaderData>this.blockHeader.toJSON()

data.nonce = solution.nonce
return BlockHeader.fromHeaderData(data, { common: this.blockHeader._common })
return BlockHeader.fromHeaderData(data, { common: this.blockHeader.common })
}

@@ -130,6 +135,6 @@ }

setTimeout(() => {
const nonce = setLengthLeft(bigIntToBuffer(this.currentNonce), 8)
const nonce = setLengthLeft(bigIntToBytes(this.currentNonce), 8)
const a = this.ethash.run(headerHash, nonce)
const result = bufferToBigInt(a.hash)
const result = bytesToBigInt(a.hash)

@@ -160,23 +165,12 @@ if (TWO_POW256 / difficulty > result) {

export type EthashCacheDB = AbstractLevel<
string | Buffer | Uint8Array,
string | Buffer,
{
cache: Buffer[]
fullSize: number
cacheSize: number
seed: Buffer
}
>
export class Ethash {
dbOpts: Object
cacheDB?: EthashCacheDB
cache: Buffer[]
cacheDB?: DB<number, DBObject>
cache: Uint8Array[]
epoc?: number
fullSize?: number
cacheSize?: number
seed?: Buffer
seed?: Uint8Array
constructor(cacheDB?: EthashCacheDB) {
constructor(cacheDB?: DB<number, DBObject>) {
this.dbOpts = {

@@ -189,10 +183,9 @@ valueEncoding: 'json',

mkcache(cacheSize: number, seed: Buffer) {
// console.log(`generating cache\nsize: ${cacheSize}\nseed: ${seed.toString('hex')}`)
mkcache(cacheSize: number, seed: Uint8Array) {
const n = Math.floor(cacheSize / params.HASH_BYTES)
const o = [Buffer.from(keccak512(seed))]
const o = [keccak512(seed)]
let i
for (i = 1; i < n; i++) {
o.push(Buffer.from(keccak512(o[o.length - 1])))
o.push(keccak512(o[o.length - 1]))
}

@@ -202,4 +195,4 @@

for (i = 0; i < n; i++) {
const v = o[i].readUInt32LE(0) % n
o[i] = Buffer.from(keccak512(xor(o[(i - 1 + n) % n], o[v])))
const v = new DataView(o[i].buffer).getUint32(0, true) % n
o[i] = keccak512(xor(o[(i - 1 + n) % n], o[v]))
}

@@ -212,16 +205,17 @@ }

calcDatasetItem(i: number): Buffer {
calcDatasetItem(i: number): Uint8Array {
const n = this.cache.length
const r = Math.floor(params.HASH_BYTES / params.WORD_BYTES)
let mix = Buffer.from(this.cache[i % n])
mix.writeInt32LE(mix.readUInt32LE(0) ^ i, 0)
mix = Buffer.from(keccak512(mix))
let mix = new Uint8Array(this.cache[i % n])
const mixView = new DataView(mix.buffer)
mixView.setUint32(0, mixView.getUint32(0, true) ^ i, true)
mix = keccak512(mix)
for (let j = 0; j < params.DATASET_PARENTS; j++) {
const cacheIndex = fnv(i ^ j, mix.readUInt32LE((j % r) * 4))
mix = fnvBuffer(mix, this.cache[cacheIndex % n])
const cacheIndex = fnv(i ^ j, new DataView(mix.buffer).getUint32((j % r) * 4, true))
mix = fnvBytes(mix, this.cache[cacheIndex % n])
}
return Buffer.from(keccak512(mix))
return keccak512(mix)
}
run(val: Buffer, nonce: Buffer, fullSize?: number) {
run(val: Uint8Array, nonce: Uint8Array, fullSize?: number) {
if (fullSize === undefined) {

@@ -236,5 +230,5 @@ if (this.fullSize === undefined) {

const w = Math.floor(params.MIX_BYTES / params.WORD_BYTES)
const s = Buffer.from(keccak512(Buffer.concat([val, bufReverse(nonce)])))
const s = keccak512(concatBytes(val, bytesReverse(nonce)))
const mixhashes = Math.floor(params.MIX_BYTES / params.HASH_BYTES)
let mix = Buffer.concat(Array(mixhashes).fill(s))
let mix = concatBytes(...Array(mixhashes).fill(s))

@@ -244,17 +238,23 @@ let i

const p =
(fnv(i ^ s.readUInt32LE(0), mix.readUInt32LE((i % w) * 4)) % Math.floor(n / mixhashes)) *
(fnv(
i ^ new DataView(s.buffer).getUint32(0, true),
new DataView(mix.buffer).getUint32((i % w) * 4, true)
) %
Math.floor(n / mixhashes)) *
mixhashes
const newdata = []
const newdata: Uint8Array[] = []
for (let j = 0; j < mixhashes; j++) {
newdata.push(this.calcDatasetItem(p + j))
}
mix = fnvBuffer(mix, Buffer.concat(newdata))
mix = fnvBytes(mix, concatBytes(...newdata))
}
const cmix = Buffer.alloc(mix.length / 4)
const cmix = new Uint8Array(mix.length / 4)
const cmixView = new DataView(cmix.buffer)
const mixView = new DataView(mix.buffer)
for (i = 0; i < mix.length / 4; i = i + 4) {
const a = fnv(mix.readUInt32LE(i * 4), mix.readUInt32LE((i + 1) * 4))
const b = fnv(a, mix.readUInt32LE((i + 2) * 4))
const c = fnv(b, mix.readUInt32LE((i + 3) * 4))
cmix.writeUInt32LE(c, i)
const a = fnv(mixView.getUint32(i * 4, true), mixView.getUint32((i + 1) * 4, true))
const b = fnv(a, mixView.getUint32((i + 2) * 4, true))
const c = fnv(b, mixView.getUint32((i + 3) * 4, true))
cmixView.setUint32(i, c, true)
}

@@ -264,3 +264,3 @@

mix: cmix,
hash: Buffer.from(keccak256(Buffer.concat([s, cmix]))),
hash: keccak256(concatBytes(s, cmix)),
}

@@ -270,7 +270,18 @@ }

cacheHash() {
return Buffer.from(keccak256(Buffer.concat(this.cache)))
// Concatenate all the cache bytes together
// We can't use `concatBytes` because calling `concatBytes(...this.cache)` results
// in a `Max call stack size exceeded` error due to the spread operator pushing all
// of the array elements onto the stack and the ethash cache can be quite large
const length = this.cache.reduce((a, arr) => a + arr.length, 0)
const result = new Uint8Array(length)
for (let i = 0, pad = 0; i < this.cache.length; i++) {
const arr = this.cache[i]
result.set(arr, pad)
pad += arr.length
}
return keccak256(result)
}
headerHash(rawHeader: Buffer[]) {
return Buffer.from(keccak256(RLP.encode(bufArrToArr(rawHeader.slice(0, -2)))))
headerHash(rawHeader: Uint8Array[]) {
return keccak256(RLP.encode(rawHeader.slice(0, -2)))
}

@@ -295,15 +306,18 @@

// gives the seed the first epoc found
const findLastSeed = async (epoc: number): Promise<[Buffer, number]> => {
const findLastSeed = async (epoc: number): Promise<[Uint8Array, number]> => {
if (epoc === 0) {
return [zeros(32), 0]
}
let data
try {
data = await this.cacheDB!.get(epoc, this.dbOpts)
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
const dbData = await this.cacheDB!.get(epoc, {
keyEncoding: KeyEncoding.Number,
valueEncoding: ValueEncoding.JSON,
})
if (dbData !== undefined) {
const data = {
cache: (dbData.cache as string[]).map((el: string) => hexToBytes(el)),
fullSize: dbData.fullSize,
cacheSize: dbData.cacheSize,
seed: hexToBytes(dbData.seed as string),
}
}
if (data) {
return [data.seed, epoc]

@@ -316,10 +330,14 @@ } else {

let data
try {
data = await this.cacheDB!.get(epoc, this.dbOpts)
} catch (error: any) {
if (error.code !== 'LEVEL_NOT_FOUND') {
throw error
const dbData = await this.cacheDB!.get(epoc, {
keyEncoding: KeyEncoding.Number,
valueEncoding: ValueEncoding.JSON,
})
if (dbData !== undefined) {
data = {
cache: (dbData.cache as string[]).map((el: string) => hexToBytes(el)),
fullSize: dbData.fullSize,
cacheSize: dbData.cacheSize,
seed: hexToBytes(dbData.seed as string),
}
}
if (!data) {

@@ -338,14 +356,17 @@ this.cacheSize = await getCacheSize(epoc)

fullSize: this.fullSize,
seed: this.seed,
cache,
seed: bytesToHex(this.seed),
cache: cache.map((el) => bytesToHex(el)),
},
this.dbOpts
{
keyEncoding: KeyEncoding.Number,
valueEncoding: ValueEncoding.JSON,
}
)
} else {
this.cache = data.cache.map((a: Buffer) => {
return Buffer.from(a)
this.cache = data.cache.map((a: Uint8Array) => {
return Uint8Array.from(a)
})
this.cacheSize = data.cacheSize
this.fullSize = data.fullSize
this.seed = Buffer.from(data.seed)
this.cacheSize = data.cacheSize as number
this.fullSize = data.fullSize as number
this.seed = Uint8Array.from(data.seed)
}

@@ -370,5 +391,4 @@ }

const a = this.run(headerHash, nonce)
const result = bufferToBigInt(a.hash)
return a.mix.equals(mixHash) && TWO_POW256 / difficulty > result
const result = bytesToBigInt(a.hash)
return equalsBytes(a.mix, mixHash) && TWO_POW256 / difficulty > result
}

@@ -375,0 +395,0 @@

import { isProbablyPrime } from 'bigint-crypto-utils'
import { keccak256 } from 'ethereum-cryptography/keccak'
import { keccak256 } from 'ethereum-cryptography/keccak.js'

@@ -47,9 +47,9 @@ export const params = {

* @method getSeed
* @param seed Buffer
* @param seed Uint8Array
* @param begin Number
* @param end Number
*/
export function getSeed(seed: Buffer, begin: number, end: number) {
export function getSeed(seed: Uint8Array, begin: number, end: number) {
for (let i = begin; i < end; i++) {
seed = Buffer.from(keccak256(seed))
seed = keccak256(seed)
}

@@ -63,6 +63,11 @@ return seed

export function fnvBuffer(a: Buffer, b: Buffer) {
const r = Buffer.alloc(a.length)
export function fnvBytes(a: Uint8Array, b: Uint8Array) {
const r = new Uint8Array(a.length)
const rView = new DataView(r.buffer)
for (let i = 0; i < a.length; i = i + 4) {
r.writeUInt32LE(fnv(a.readUInt32LE(i), b.readUInt32LE(i)), i)
rView.setUint32(
i,
fnv(new DataView(a.buffer).getUint32(i, true), new DataView(b.buffer).getUint32(i, true)),
true
)
}

@@ -72,5 +77,5 @@ return r

export function bufReverse(a: Buffer) {
export function bytesReverse(a: Uint8Array) {
const length = a.length
const b = Buffer.alloc(length)
const b = new Uint8Array(length)
for (let i = 0; i < length; i++) {

@@ -77,0 +82,0 @@ b[i] = a[length - i - 1]

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc