@solana/web3.js 1.54.0 → 1.54.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana/web3.js",
3
- "version": "1.54.0",
3
+ "version": "1.54.2",
4
4
  "description": "Solana Javascript API",
5
5
  "keywords": [
6
6
  "api",
@@ -36,29 +36,11 @@
36
36
  "/lib",
37
37
  "/src"
38
38
  ],
39
- "scripts": {
40
- "build": "npm run clean; cross-env NODE_ENV=production rollup -c; npm run type:gen",
41
- "build:fixtures": "set -ex; ./test/fixtures/noop-program/build.sh",
42
- "clean": "rimraf ./coverage ./lib",
43
- "codecov": "set -ex; npm run test:cover; cat ./coverage/lcov.info | codecov",
44
- "dev": "cross-env NODE_ENV=development rollup -c",
45
- "doc": "set -ex; typedoc --treatWarningsAsErrors",
46
- "type:gen": "./scripts/typegen.sh",
47
- "lint": "set -ex; npm run pretty; eslint . --ext .js,.ts",
48
- "lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts",
49
- "type:check": "tsc -p tsconfig.json --noEmit",
50
- "ok": "run-s lint test doc type:check",
51
- "pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
52
- "pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
53
- "re": "semantic-release --repository-url git@github.com:solana-labs/solana-web3.js.git",
54
- "test": "cross-env TS_NODE_COMPILER_OPTIONS='{ \"module\": \"commonjs\", \"target\": \"es2019\" }' ts-mocha --require esm './test/**/*.test.ts'",
55
- "test:cover": "nyc --reporter=lcov npm run test",
56
- "test:live": "TEST_LIVE=1 npm run test",
57
- "test:live-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:live"
58
- },
59
39
  "dependencies": {
60
40
  "@babel/runtime": "^7.12.5",
61
- "@ethersproject/sha2": "^5.5.0",
41
+ "@noble/ed25519": "^1.7.0",
42
+ "@noble/hashes": "^1.1.2",
43
+ "@noble/secp256k1": "^1.6.3",
62
44
  "@solana/buffer-layout": "^4.0.0",
63
45
  "bigint-buffer": "^1.1.5",
64
46
  "bn.js": "^5.0.0",
@@ -70,9 +52,7 @@
70
52
  "js-sha3": "^0.8.0",
71
53
  "node-fetch": "2",
72
54
  "rpc-websockets": "^7.5.0",
73
- "secp256k1": "^4.0.2",
74
- "superstruct": "^0.14.2",
75
- "tweetnacl": "^1.0.3"
55
+ "superstruct": "^0.14.2"
76
56
  },
77
57
  "devDependencies": {
78
58
  "@babel/core": "^7.12.13",
@@ -100,7 +80,6 @@
100
80
  "@types/mz": "^2.7.3",
101
81
  "@types/node": "^17.0.24",
102
82
  "@types/node-fetch": "2",
103
- "@types/secp256k1": "^4.0.1",
104
83
  "@types/sinon": "^10.0.0",
105
84
  "@types/sinon-chai": "^3.2.8",
106
85
  "@typescript-eslint/eslint-plugin": "^4.14.2",
@@ -139,5 +118,25 @@
139
118
  },
140
119
  "engines": {
141
120
  "node": ">=12.20.0"
121
+ },
122
+ "scripts": {
123
+ "build": "npm run clean; cross-env NODE_ENV=production rollup -c; npm run type:gen",
124
+ "build:fixtures": "set -ex; ./test/fixtures/noop-program/build.sh",
125
+ "clean": "rimraf ./coverage ./lib",
126
+ "codecov": "set -ex; npm run test:cover; cat ./coverage/lcov.info | codecov",
127
+ "dev": "cross-env NODE_ENV=development rollup -c",
128
+ "doc": "set -ex; typedoc --treatWarningsAsErrors",
129
+ "type:gen": "./scripts/typegen.sh",
130
+ "lint": "set -ex; npm run pretty; eslint . --ext .js,.ts",
131
+ "lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts",
132
+ "type:check": "tsc -p tsconfig.json --noEmit",
133
+ "ok": "run-s lint test doc type:check",
134
+ "pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'",
135
+ "pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'",
136
+ "re": "semantic-release --repository-url git@github.com:solana-labs/solana-web3.js.git",
137
+ "test": "cross-env TS_NODE_COMPILER_OPTIONS='{ \"module\": \"commonjs\", \"target\": \"es2019\" }' ts-mocha --require esm './test/**/*.test.ts'",
138
+ "test:cover": "nyc --reporter=lcov npm run test",
139
+ "test:live": "TEST_LIVE=1 npm run test",
140
+ "test:live-with-test-validator": "start-server-and-test 'solana-test-validator --reset --quiet' http://localhost:8899/health test:live"
142
141
  }
143
- }
142
+ }
package/src/account.ts CHANGED
@@ -1,7 +1,6 @@
1
- import nacl from 'tweetnacl';
2
- import type {SignKeyPair as KeyPair} from 'tweetnacl';
3
- import type {Buffer} from 'buffer';
1
+ import {Buffer} from 'buffer';
4
2
 
3
+ import {generatePrivateKey, getPublicKey} from './utils/ed25519';
5
4
  import {toBuffer} from './utils/to-buffer';
6
5
  import {PublicKey} from './publickey';
7
6
 
@@ -12,7 +11,9 @@ import {PublicKey} from './publickey';
12
11
  */
13
12
  export class Account {
14
13
  /** @internal */
15
- _keypair: KeyPair;
14
+ private _publicKey: Buffer;
15
+ /** @internal */
16
+ private _secretKey: Buffer;
16
17
 
17
18
  /**
18
19
  * Create a new Account object
@@ -24,9 +25,15 @@ export class Account {
24
25
  */
25
26
  constructor(secretKey?: Buffer | Uint8Array | Array<number>) {
26
27
  if (secretKey) {
27
- this._keypair = nacl.sign.keyPair.fromSecretKey(toBuffer(secretKey));
28
+ const secretKeyBuffer = toBuffer(secretKey);
29
+ if (secretKey.length !== 64) {
30
+ throw new Error('bad secret key size');
31
+ }
32
+ this._publicKey = secretKeyBuffer.slice(32, 64);
33
+ this._secretKey = secretKeyBuffer.slice(0, 32);
28
34
  } else {
29
- this._keypair = nacl.sign.keyPair();
35
+ this._secretKey = toBuffer(generatePrivateKey());
36
+ this._publicKey = toBuffer(getPublicKey(this._secretKey));
30
37
  }
31
38
  }
32
39
 
@@ -34,13 +41,15 @@ export class Account {
34
41
  * The public key for this account
35
42
  */
36
43
  get publicKey(): PublicKey {
37
- return new PublicKey(this._keypair.publicKey);
44
+ return new PublicKey(this._publicKey);
38
45
  }
39
46
 
40
47
  /**
41
- * The **unencrypted** secret key for this account
48
+ * The **unencrypted** secret key for this account. The first 32 bytes
49
+ * is the private scalar and the last 32 bytes is the public key.
50
+ * Read more: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
42
51
  */
43
52
  get secretKey(): Buffer {
44
- return toBuffer(this._keypair.secretKey);
53
+ return Buffer.concat([this._secretKey, this._publicKey], 64);
45
54
  }
46
55
  }
package/src/keypair.ts CHANGED
@@ -1,5 +1,4 @@
1
- import nacl from 'tweetnacl';
2
-
1
+ import {generateKeypair, getPublicKey, Ed25519Keypair} from './utils/ed25519';
3
2
  import {PublicKey} from './publickey';
4
3
 
5
4
  /**
@@ -10,14 +9,6 @@ export interface Signer {
10
9
  secretKey: Uint8Array;
11
10
  }
12
11
 
13
- /**
14
- * Ed25519 Keypair
15
- */
16
- export interface Ed25519Keypair {
17
- publicKey: Uint8Array;
18
- secretKey: Uint8Array;
19
- }
20
-
21
12
  /**
22
13
  * An account keypair used for signing transactions.
23
14
  */
@@ -31,18 +22,14 @@ export class Keypair {
31
22
  * @param keypair ed25519 keypair
32
23
  */
33
24
  constructor(keypair?: Ed25519Keypair) {
34
- if (keypair) {
35
- this._keypair = keypair;
36
- } else {
37
- this._keypair = nacl.sign.keyPair();
38
- }
25
+ this._keypair = keypair ?? generateKeypair();
39
26
  }
40
27
 
41
28
  /**
42
29
  * Generate a new random keypair
43
30
  */
44
31
  static generate(): Keypair {
45
- return new Keypair(nacl.sign.keyPair());
32
+ return new Keypair(generateKeypair());
46
33
  }
47
34
 
48
35
  /**
@@ -61,16 +48,20 @@ export class Keypair {
61
48
  secretKey: Uint8Array,
62
49
  options?: {skipValidation?: boolean},
63
50
  ): Keypair {
64
- const keypair = nacl.sign.keyPair.fromSecretKey(secretKey);
51
+ if (secretKey.byteLength !== 64) {
52
+ throw new Error('bad secret key size');
53
+ }
54
+ const publicKey = secretKey.slice(32, 64);
65
55
  if (!options || !options.skipValidation) {
66
- const encoder = new TextEncoder();
67
- const signData = encoder.encode('@solana/web3.js-validation-v1');
68
- const signature = nacl.sign.detached(signData, keypair.secretKey);
69
- if (!nacl.sign.detached.verify(signData, signature, keypair.publicKey)) {
70
- throw new Error('provided secretKey is invalid');
56
+ const privateScalar = secretKey.slice(0, 32);
57
+ const computedPublicKey = getPublicKey(privateScalar);
58
+ for (let ii = 0; ii < 32; ii++) {
59
+ if (publicKey[ii] !== computedPublicKey[ii]) {
60
+ throw new Error('provided secretKey is invalid');
61
+ }
71
62
  }
72
63
  }
73
- return new Keypair(keypair);
64
+ return new Keypair({publicKey, secretKey});
74
65
  }
75
66
 
76
67
  /**
@@ -79,7 +70,11 @@ export class Keypair {
79
70
  * @param seed seed byte array
80
71
  */
81
72
  static fromSeed(seed: Uint8Array): Keypair {
82
- return new Keypair(nacl.sign.keyPair.fromSeed(seed));
73
+ const publicKey = getPublicKey(seed);
74
+ const secretKey = new Uint8Array(64);
75
+ secretKey.set(seed);
76
+ secretKey.set(publicKey, 32);
77
+ return new Keypair({publicKey, secretKey});
83
78
  }
84
79
 
85
80
  /**
@@ -13,6 +13,7 @@ import {
13
13
  MessageAddressTableLookup,
14
14
  MessageCompiledInstruction,
15
15
  } from './index';
16
+ import {guardedShift, guardedSplice} from '../utils/guarded-array-utils';
16
17
 
17
18
  /**
18
19
  * An instruction to execute by a program
@@ -232,7 +233,7 @@ export class Message {
232
233
  // Slice up wire data
233
234
  let byteArray = [...buffer];
234
235
 
235
- const numRequiredSignatures = byteArray.shift() as number;
236
+ const numRequiredSignatures = guardedShift(byteArray);
236
237
  if (
237
238
  numRequiredSignatures !==
238
239
  (numRequiredSignatures & VERSION_PREFIX_MASK)
@@ -242,31 +243,27 @@ export class Message {
242
243
  );
243
244
  }
244
245
 
245
- const numReadonlySignedAccounts = byteArray.shift() as number;
246
- const numReadonlyUnsignedAccounts = byteArray.shift() as number;
246
+ const numReadonlySignedAccounts = guardedShift(byteArray);
247
+ const numReadonlyUnsignedAccounts = guardedShift(byteArray);
247
248
 
248
249
  const accountCount = shortvec.decodeLength(byteArray);
249
250
  let accountKeys = [];
250
251
  for (let i = 0; i < accountCount; i++) {
251
- const account = byteArray.slice(0, PUBLIC_KEY_LENGTH);
252
- byteArray = byteArray.slice(PUBLIC_KEY_LENGTH);
252
+ const account = guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH);
253
253
  accountKeys.push(bs58.encode(Buffer.from(account)));
254
254
  }
255
255
 
256
- const recentBlockhash = byteArray.slice(0, PUBLIC_KEY_LENGTH);
257
- byteArray = byteArray.slice(PUBLIC_KEY_LENGTH);
256
+ const recentBlockhash = guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH);
258
257
 
259
258
  const instructionCount = shortvec.decodeLength(byteArray);
260
259
  let instructions: CompiledInstruction[] = [];
261
260
  for (let i = 0; i < instructionCount; i++) {
262
- const programIdIndex = byteArray.shift() as number;
261
+ const programIdIndex = guardedShift(byteArray);
263
262
  const accountCount = shortvec.decodeLength(byteArray);
264
- const accounts = byteArray.slice(0, accountCount);
265
- byteArray = byteArray.slice(accountCount);
263
+ const accounts = guardedSplice(byteArray, 0, accountCount);
266
264
  const dataLength = shortvec.decodeLength(byteArray);
267
- const dataSlice = byteArray.slice(0, dataLength);
265
+ const dataSlice = guardedSplice(byteArray, 0, dataLength);
268
266
  const data = bs58.encode(Buffer.from(dataSlice));
269
- byteArray = byteArray.slice(dataLength);
270
267
  instructions.push({
271
268
  programIdIndex,
272
269
  accounts,
package/src/message/v0.ts CHANGED
@@ -12,6 +12,7 @@ import {PublicKey, PUBLIC_KEY_LENGTH} from '../publickey';
12
12
  import * as shortvec from '../utils/shortvec-encoding';
13
13
  import assert from '../utils/assert';
14
14
  import {PACKET_DATA_SIZE, VERSION_PREFIX_MASK} from '../transaction/constants';
15
+ import {guardedShift, guardedSplice} from '../utils/guarded-array-utils';
15
16
 
16
17
  /**
17
18
  * Message constructor arguments
@@ -254,7 +255,7 @@ export class MessageV0 {
254
255
  static deserialize(serializedMessage: Uint8Array): MessageV0 {
255
256
  let byteArray = [...serializedMessage];
256
257
 
257
- const prefix = byteArray.shift() as number;
258
+ const prefix = guardedShift(byteArray);
258
259
  const maskedPrefix = prefix & VERSION_PREFIX_MASK;
259
260
  assert(
260
261
  prefix !== maskedPrefix,
@@ -268,29 +269,35 @@ export class MessageV0 {
268
269
  );
269
270
 
270
271
  const header: MessageHeader = {
271
- numRequiredSignatures: byteArray.shift() as number,
272
- numReadonlySignedAccounts: byteArray.shift() as number,
273
- numReadonlyUnsignedAccounts: byteArray.shift() as number,
272
+ numRequiredSignatures: guardedShift(byteArray),
273
+ numReadonlySignedAccounts: guardedShift(byteArray),
274
+ numReadonlyUnsignedAccounts: guardedShift(byteArray),
274
275
  };
275
276
 
276
277
  const staticAccountKeys = [];
277
278
  const staticAccountKeysLength = shortvec.decodeLength(byteArray);
278
279
  for (let i = 0; i < staticAccountKeysLength; i++) {
279
280
  staticAccountKeys.push(
280
- new PublicKey(byteArray.splice(0, PUBLIC_KEY_LENGTH)),
281
+ new PublicKey(guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH)),
281
282
  );
282
283
  }
283
284
 
284
- const recentBlockhash = bs58.encode(byteArray.splice(0, PUBLIC_KEY_LENGTH));
285
+ const recentBlockhash = bs58.encode(
286
+ guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH),
287
+ );
285
288
 
286
289
  const instructionCount = shortvec.decodeLength(byteArray);
287
290
  const compiledInstructions: MessageCompiledInstruction[] = [];
288
291
  for (let i = 0; i < instructionCount; i++) {
289
- const programIdIndex = byteArray.shift() as number;
292
+ const programIdIndex = guardedShift(byteArray);
290
293
  const accountKeyIndexesLength = shortvec.decodeLength(byteArray);
291
- const accountKeyIndexes = byteArray.splice(0, accountKeyIndexesLength);
294
+ const accountKeyIndexes = guardedSplice(
295
+ byteArray,
296
+ 0,
297
+ accountKeyIndexesLength,
298
+ );
292
299
  const dataLength = shortvec.decodeLength(byteArray);
293
- const data = new Uint8Array(byteArray.splice(0, dataLength));
300
+ const data = new Uint8Array(guardedSplice(byteArray, 0, dataLength));
294
301
  compiledInstructions.push({
295
302
  programIdIndex,
296
303
  accountKeyIndexes,
@@ -301,11 +308,21 @@ export class MessageV0 {
301
308
  const addressTableLookupsCount = shortvec.decodeLength(byteArray);
302
309
  const addressTableLookups: MessageAddressTableLookup[] = [];
303
310
  for (let i = 0; i < addressTableLookupsCount; i++) {
304
- const accountKey = new PublicKey(byteArray.splice(0, PUBLIC_KEY_LENGTH));
311
+ const accountKey = new PublicKey(
312
+ guardedSplice(byteArray, 0, PUBLIC_KEY_LENGTH),
313
+ );
305
314
  const writableIndexesLength = shortvec.decodeLength(byteArray);
306
- const writableIndexes = byteArray.splice(0, writableIndexesLength);
315
+ const writableIndexes = guardedSplice(
316
+ byteArray,
317
+ 0,
318
+ writableIndexesLength,
319
+ );
307
320
  const readonlyIndexesLength = shortvec.decodeLength(byteArray);
308
- const readonlyIndexes = byteArray.splice(0, readonlyIndexesLength);
321
+ const readonlyIndexes = guardedSplice(
322
+ byteArray,
323
+ 0,
324
+ readonlyIndexesLength,
325
+ );
309
326
  addressTableLookups.push({
310
327
  accountKey,
311
328
  writableIndexes,
@@ -1,11 +1,11 @@
1
1
  import {Buffer} from 'buffer';
2
2
  import * as BufferLayout from '@solana/buffer-layout';
3
- import nacl from 'tweetnacl';
4
3
 
5
4
  import {Keypair} from '../keypair';
6
5
  import {PublicKey} from '../publickey';
7
6
  import {TransactionInstruction} from '../transaction';
8
7
  import assert from '../utils/assert';
8
+ import {sign} from '../utils/ed25519';
9
9
 
10
10
  const PRIVATE_KEY_BYTES = 64;
11
11
  const PUBLIC_KEY_BYTES = 32;
@@ -142,7 +142,7 @@ export class Ed25519Program {
142
142
  try {
143
143
  const keypair = Keypair.fromSecretKey(privateKey);
144
144
  const publicKey = keypair.publicKey.toBytes();
145
- const signature = nacl.sign.detached(message, keypair.secretKey);
145
+ const signature = sign(message, keypair.secretKey);
146
146
 
147
147
  return this.createInstructionWithPublicKey({
148
148
  publicKey,
@@ -1,15 +1,13 @@
1
1
  import {Buffer} from 'buffer';
2
2
  import * as BufferLayout from '@solana/buffer-layout';
3
- import secp256k1 from 'secp256k1';
4
3
  import sha3 from 'js-sha3';
5
4
 
6
5
  import {PublicKey} from '../publickey';
7
6
  import {TransactionInstruction} from '../transaction';
8
7
  import assert from '../utils/assert';
8
+ import {publicKeyCreate, ecdsaSign} from '../utils/secp256k1';
9
9
  import {toBuffer} from '../utils/to-buffer';
10
10
 
11
- const {publicKeyCreate, ecdsaSign} = secp256k1;
12
-
13
11
  const PRIVATE_KEY_BYTES = 32;
14
12
  const ETHEREUM_ADDRESS_BYTES = 20;
15
13
  const PUBLIC_KEY_BYTES = 64;
@@ -209,11 +207,14 @@ export class Secp256k1Program {
209
207
 
210
208
  try {
211
209
  const privateKey = toBuffer(pkey);
212
- const publicKey = publicKeyCreate(privateKey, false).slice(1); // throw away leading byte
210
+ const publicKey = publicKeyCreate(
211
+ privateKey,
212
+ false /* isCompressed */,
213
+ ).slice(1); // throw away leading byte
213
214
  const messageHash = Buffer.from(
214
215
  sha3.keccak_256.update(toBuffer(message)).digest(),
215
216
  );
216
- const {signature, recid: recoveryId} = ecdsaSign(messageHash, privateKey);
217
+ const [signature, recoveryId] = ecdsaSign(messageHash, privateKey);
217
218
 
218
219
  return this.createInstructionWithPublicKey({
219
220
  publicKey,
package/src/publickey.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import BN from 'bn.js';
2
2
  import bs58 from 'bs58';
3
3
  import {Buffer} from 'buffer';
4
- import nacl from 'tweetnacl';
5
- import {sha256} from '@ethersproject/sha2';
4
+ import {sha256} from '@noble/hashes/sha256';
6
5
 
6
+ import {isOnCurve} from './utils/ed25519';
7
7
  import {Struct, SOLANA_SCHEMA} from './utils/borsh-schema';
8
8
  import {toBuffer} from './utils/to-buffer';
9
9
 
@@ -140,8 +140,8 @@ export class PublicKey extends Struct {
140
140
  Buffer.from(seed),
141
141
  programId.toBuffer(),
142
142
  ]);
143
- const hash = sha256(new Uint8Array(buffer)).slice(2);
144
- return new PublicKey(Buffer.from(hash, 'hex'));
143
+ const publicKeyBytes = sha256(buffer);
144
+ return new PublicKey(publicKeyBytes);
145
145
  }
146
146
 
147
147
  /**
@@ -164,9 +164,8 @@ export class PublicKey extends Struct {
164
164
  programId.toBuffer(),
165
165
  Buffer.from('ProgramDerivedAddress'),
166
166
  ]);
167
- let hash = sha256(new Uint8Array(buffer)).slice(2);
168
- let publicKeyBytes = new BN(hash, 16).toArray(undefined, 32);
169
- if (is_on_curve(publicKeyBytes)) {
167
+ const publicKeyBytes = sha256(buffer);
168
+ if (isOnCurve(publicKeyBytes)) {
170
169
  throw new Error(`Invalid seeds, address must fall off the curve`);
171
170
  }
172
171
  return new PublicKey(publicKeyBytes);
@@ -229,7 +228,7 @@ export class PublicKey extends Struct {
229
228
  */
230
229
  static isOnCurve(pubkeyData: PublicKeyInitData): boolean {
231
230
  const pubkey = new PublicKey(pubkeyData);
232
- return is_on_curve(pubkey.toBytes()) == 1;
231
+ return isOnCurve(pubkey.toBytes());
233
232
  }
234
233
  }
235
234
 
@@ -237,66 +236,3 @@ SOLANA_SCHEMA.set(PublicKey, {
237
236
  kind: 'struct',
238
237
  fields: [['_bn', 'u256']],
239
238
  });
240
-
241
- // @ts-ignore
242
- let naclLowLevel = nacl.lowlevel;
243
-
244
- // Check that a pubkey is on the curve.
245
- // This function and its dependents were sourced from:
246
- // https://github.com/dchest/tweetnacl-js/blob/f1ec050ceae0861f34280e62498b1d3ed9c350c6/nacl.js#L792
247
- function is_on_curve(p: any) {
248
- var r = [
249
- naclLowLevel.gf(),
250
- naclLowLevel.gf(),
251
- naclLowLevel.gf(),
252
- naclLowLevel.gf(),
253
- ];
254
-
255
- var t = naclLowLevel.gf(),
256
- chk = naclLowLevel.gf(),
257
- num = naclLowLevel.gf(),
258
- den = naclLowLevel.gf(),
259
- den2 = naclLowLevel.gf(),
260
- den4 = naclLowLevel.gf(),
261
- den6 = naclLowLevel.gf();
262
-
263
- naclLowLevel.set25519(r[2], gf1);
264
- naclLowLevel.unpack25519(r[1], p);
265
- naclLowLevel.S(num, r[1]);
266
- naclLowLevel.M(den, num, naclLowLevel.D);
267
- naclLowLevel.Z(num, num, r[2]);
268
- naclLowLevel.A(den, r[2], den);
269
-
270
- naclLowLevel.S(den2, den);
271
- naclLowLevel.S(den4, den2);
272
- naclLowLevel.M(den6, den4, den2);
273
- naclLowLevel.M(t, den6, num);
274
- naclLowLevel.M(t, t, den);
275
-
276
- naclLowLevel.pow2523(t, t);
277
- naclLowLevel.M(t, t, num);
278
- naclLowLevel.M(t, t, den);
279
- naclLowLevel.M(t, t, den);
280
- naclLowLevel.M(r[0], t, den);
281
-
282
- naclLowLevel.S(chk, r[0]);
283
- naclLowLevel.M(chk, chk, den);
284
- if (neq25519(chk, num)) naclLowLevel.M(r[0], r[0], I);
285
-
286
- naclLowLevel.S(chk, r[0]);
287
- naclLowLevel.M(chk, chk, den);
288
- if (neq25519(chk, num)) return 0;
289
- return 1;
290
- }
291
- let gf1 = naclLowLevel.gf([1]);
292
- let I = naclLowLevel.gf([
293
- 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7,
294
- 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
295
- ]);
296
- function neq25519(a: any, b: any) {
297
- var c = new Uint8Array(32),
298
- d = new Uint8Array(32);
299
- naclLowLevel.pack25519(c, a);
300
- naclLowLevel.pack25519(d, b);
301
- return naclLowLevel.crypto_verify_32(c, 0, d, 0);
302
- }
@@ -1,4 +1,3 @@
1
- import nacl from 'tweetnacl';
2
1
  import bs58 from 'bs58';
3
2
  import {Buffer} from 'buffer';
4
3
 
@@ -12,6 +11,8 @@ import invariant from '../utils/assert';
12
11
  import type {Signer} from '../keypair';
13
12
  import type {Blockhash} from '../blockhash';
14
13
  import type {CompiledInstruction} from '../message';
14
+ import {sign, verify} from '../utils/ed25519';
15
+ import {guardedSplice} from '../utils/guarded-array-utils';
15
16
 
16
17
  /**
17
18
  * Transaction signature as base-58 encoded string
@@ -658,7 +659,7 @@ export class Transaction {
658
659
  _partialSign(message: Message, ...signers: Array<Signer>) {
659
660
  const signData = message.serialize();
660
661
  signers.forEach(signer => {
661
- const signature = nacl.sign.detached(signData, signer.secretKey);
662
+ const signature = sign(signData, signer.secretKey);
662
663
  this._addSignature(signer.publicKey, toBuffer(signature));
663
664
  });
664
665
  }
@@ -706,9 +707,7 @@ export class Transaction {
706
707
  return false;
707
708
  }
708
709
  } else {
709
- if (
710
- !nacl.sign.detached.verify(signData, signature, publicKey.toBuffer())
711
- ) {
710
+ if (!verify(signature, signData, publicKey.toBuffer())) {
712
711
  return false;
713
712
  }
714
713
  }
@@ -805,8 +804,7 @@ export class Transaction {
805
804
  const signatureCount = shortvec.decodeLength(byteArray);
806
805
  let signatures = [];
807
806
  for (let i = 0; i < signatureCount; i++) {
808
- const signature = byteArray.slice(0, SIGNATURE_LENGTH_IN_BYTES);
809
- byteArray = byteArray.slice(SIGNATURE_LENGTH_IN_BYTES);
807
+ const signature = guardedSplice(byteArray, 0, SIGNATURE_LENGTH_IN_BYTES);
810
808
  signatures.push(bs58.encode(Buffer.from(signature)));
811
809
  }
812
810
 
@@ -1,4 +1,3 @@
1
- import nacl from 'tweetnacl';
2
1
  import * as BufferLayout from '@solana/buffer-layout';
3
2
 
4
3
  import {Signer} from '../keypair';
@@ -7,6 +6,8 @@ import {VersionedMessage} from '../message/versioned';
7
6
  import {SIGNATURE_LENGTH_IN_BYTES} from './constants';
8
7
  import * as shortvec from '../utils/shortvec-encoding';
9
8
  import * as Layout from '../layout';
9
+ import {sign} from '../utils/ed25519';
10
+ import {guardedSplice} from '../utils/guarded-array-utils';
10
11
 
11
12
  export type TransactionVersion = 'legacy' | 0;
12
13
 
@@ -77,7 +78,7 @@ export class VersionedTransaction {
77
78
  const signaturesLength = shortvec.decodeLength(byteArray);
78
79
  for (let i = 0; i < signaturesLength; i++) {
79
80
  signatures.push(
80
- new Uint8Array(byteArray.splice(0, SIGNATURE_LENGTH_IN_BYTES)),
81
+ new Uint8Array(guardedSplice(byteArray, 0, SIGNATURE_LENGTH_IN_BYTES)),
81
82
  );
82
83
  }
83
84
 
@@ -99,10 +100,7 @@ export class VersionedTransaction {
99
100
  signerIndex >= 0,
100
101
  `Cannot sign with non signer key ${signer.publicKey.toBase58()}`,
101
102
  );
102
- this.signatures[signerIndex] = nacl.sign.detached(
103
- messageData,
104
- signer.secretKey,
105
- );
103
+ this.signatures[signerIndex] = sign(messageData, signer.secretKey);
106
104
  }
107
105
  }
108
106
  }
@@ -0,0 +1,46 @@
1
+ import {sha512} from '@noble/hashes/sha512';
2
+ import * as ed25519 from '@noble/ed25519';
3
+
4
+ /**
5
+ * A 64 byte secret key, the first 32 bytes of which is the
6
+ * private scalar and the last 32 bytes is the public key.
7
+ * Read more: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
8
+ */
9
+ type Ed25519SecretKey = Uint8Array;
10
+
11
+ /**
12
+ * Ed25519 Keypair
13
+ */
14
+ export interface Ed25519Keypair {
15
+ publicKey: Uint8Array;
16
+ secretKey: Ed25519SecretKey;
17
+ }
18
+
19
+ ed25519.utils.sha512Sync = (...m) => sha512(ed25519.utils.concatBytes(...m));
20
+
21
+ export const generatePrivateKey = ed25519.utils.randomPrivateKey;
22
+ export const generateKeypair = (): Ed25519Keypair => {
23
+ const privateScalar = ed25519.utils.randomPrivateKey();
24
+ const publicKey = getPublicKey(privateScalar);
25
+ const secretKey = new Uint8Array(64);
26
+ secretKey.set(privateScalar);
27
+ secretKey.set(publicKey, 32);
28
+ return {
29
+ publicKey,
30
+ secretKey,
31
+ };
32
+ };
33
+ export const getPublicKey = ed25519.sync.getPublicKey;
34
+ export function isOnCurve(publicKey: Uint8Array): boolean {
35
+ try {
36
+ ed25519.Point.fromHex(publicKey, true /* strict */);
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+ export const sign = (
43
+ message: Parameters<typeof ed25519.sync.sign>[0],
44
+ secretKey: Ed25519SecretKey,
45
+ ) => ed25519.sync.sign(message, secretKey.slice(0, 32));
46
+ export const verify = ed25519.sync.verify;