fix: replace tweetnacl impl
* Install `@noble/ed25519` and create a shim * Replace `tweetnacl` with `@noble/ed25519` in `Account` class * Replace `tweetnacl` with `@noble/ed25519` in `Keypair` class * Replace `tweetnacl` with `@noble/ed25519` in `PublicKey` class * Replace `tweetnacl` with `@noble/ed25519` in `Ed25519Program` class * Replace `tweetnacl` with `@noble/ed25519` in `Transaction` class * Replace `tweetnacl` with `@noble/ed25519` in versioned `Transaction` class * Remove `tweetnacl` from project * Damnit, typedoc.
This commit is contained in:
parent
ad6b280b0a
commit
60f3dc5f72
|
@ -10,6 +10,7 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@noble/ed25519": "^1.7.0",
|
||||||
"@noble/hashes": "^1.1.2",
|
"@noble/hashes": "^1.1.2",
|
||||||
"@noble/secp256k1": "^1.6.3",
|
"@noble/secp256k1": "^1.6.3",
|
||||||
"@solana/buffer-layout": "^4.0.0",
|
"@solana/buffer-layout": "^4.0.0",
|
||||||
|
@ -23,8 +24,7 @@
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"rpc-websockets": "^7.5.0",
|
"rpc-websockets": "^7.5.0",
|
||||||
"superstruct": "^0.14.2",
|
"superstruct": "^0.14.2"
|
||||||
"tweetnacl": "^1.0.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.13",
|
"@babel/core": "^7.12.13",
|
||||||
|
@ -2754,6 +2754,17 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@noble/ed25519": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@noble/hashes": {
|
"node_modules/@noble/hashes": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
|
||||||
|
@ -15849,7 +15860,8 @@
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
|
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
|
@ -18564,6 +18576,11 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@noble/ed25519": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg=="
|
||||||
|
},
|
||||||
"@noble/hashes": {
|
"@noble/hashes": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
|
||||||
|
@ -28560,7 +28577,8 @@
|
||||||
"tweetnacl": {
|
"tweetnacl": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
|
||||||
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="
|
"integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"type-check": {
|
"type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
|
"@noble/ed25519": "^1.7.0",
|
||||||
"@noble/hashes": "^1.1.2",
|
"@noble/hashes": "^1.1.2",
|
||||||
"@noble/secp256k1": "^1.6.3",
|
"@noble/secp256k1": "^1.6.3",
|
||||||
"@solana/buffer-layout": "^4.0.0",
|
"@solana/buffer-layout": "^4.0.0",
|
||||||
|
@ -71,8 +72,7 @@
|
||||||
"js-sha3": "^0.8.0",
|
"js-sha3": "^0.8.0",
|
||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"rpc-websockets": "^7.5.0",
|
"rpc-websockets": "^7.5.0",
|
||||||
"superstruct": "^0.14.2",
|
"superstruct": "^0.14.2"
|
||||||
"tweetnacl": "^1.0.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.13",
|
"@babel/core": "^7.12.13",
|
||||||
|
|
|
@ -97,6 +97,8 @@ function generateConfig(configType, format) {
|
||||||
/@babel\/runtime/,
|
/@babel\/runtime/,
|
||||||
'@noble/hashes/hmac',
|
'@noble/hashes/hmac',
|
||||||
'@noble/hashes/sha256',
|
'@noble/hashes/sha256',
|
||||||
|
'@noble/hashes/sha512',
|
||||||
|
'@noble/ed25519',
|
||||||
'@noble/secp256k1',
|
'@noble/secp256k1',
|
||||||
'@solana/buffer-layout',
|
'@solana/buffer-layout',
|
||||||
'bigint-buffer',
|
'bigint-buffer',
|
||||||
|
@ -110,7 +112,6 @@ function generateConfig(configType, format) {
|
||||||
'node-fetch',
|
'node-fetch',
|
||||||
'rpc-websockets',
|
'rpc-websockets',
|
||||||
'superstruct',
|
'superstruct',
|
||||||
'tweetnacl',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +164,8 @@ function generateConfig(configType, format) {
|
||||||
'@solana/buffer-layout',
|
'@solana/buffer-layout',
|
||||||
'@noble/hashes/hmac',
|
'@noble/hashes/hmac',
|
||||||
'@noble/hashes/sha256',
|
'@noble/hashes/sha256',
|
||||||
|
'@noble/hashes/sha512',
|
||||||
|
'@noble/ed25519',
|
||||||
'@noble/secp256k1',
|
'@noble/secp256k1',
|
||||||
'bigint-buffer',
|
'bigint-buffer',
|
||||||
'bn.js',
|
'bn.js',
|
||||||
|
@ -178,7 +181,6 @@ function generateConfig(configType, format) {
|
||||||
'react-native-url-polyfill',
|
'react-native-url-polyfill',
|
||||||
'rpc-websockets',
|
'rpc-websockets',
|
||||||
'superstruct',
|
'superstruct',
|
||||||
'tweetnacl',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import nacl from 'tweetnacl';
|
import {Buffer} from 'buffer';
|
||||||
import type {SignKeyPair as KeyPair} from 'tweetnacl';
|
|
||||||
import type {Buffer} from 'buffer';
|
|
||||||
|
|
||||||
|
import {generatePrivateKey, getPublicKey} from './utils/ed25519';
|
||||||
import {toBuffer} from './utils/to-buffer';
|
import {toBuffer} from './utils/to-buffer';
|
||||||
import {PublicKey} from './publickey';
|
import {PublicKey} from './publickey';
|
||||||
|
|
||||||
|
@ -12,7 +11,9 @@ import {PublicKey} from './publickey';
|
||||||
*/
|
*/
|
||||||
export class Account {
|
export class Account {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_keypair: KeyPair;
|
private _publicKey: Buffer;
|
||||||
|
/** @internal */
|
||||||
|
private _secretKey: Buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Account object
|
* Create a new Account object
|
||||||
|
@ -24,9 +25,15 @@ export class Account {
|
||||||
*/
|
*/
|
||||||
constructor(secretKey?: Buffer | Uint8Array | Array<number>) {
|
constructor(secretKey?: Buffer | Uint8Array | Array<number>) {
|
||||||
if (secretKey) {
|
if (secretKey) {
|
||||||
this._keypair = nacl.sign.keyPair.fromSecretKey(toBuffer(secretKey));
|
const secretKeyBuffer = toBuffer(secretKey);
|
||||||
|
if (secretKey.length !== 64) {
|
||||||
|
throw new Error('bad secret key size');
|
||||||
|
}
|
||||||
|
this._publicKey = secretKeyBuffer.slice(32, 64);
|
||||||
|
this._secretKey = secretKeyBuffer.slice(0, 32);
|
||||||
} else {
|
} else {
|
||||||
this._keypair = nacl.sign.keyPair();
|
this._secretKey = toBuffer(generatePrivateKey());
|
||||||
|
this._publicKey = toBuffer(getPublicKey(this._secretKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +41,15 @@ export class Account {
|
||||||
* The public key for this account
|
* The public key for this account
|
||||||
*/
|
*/
|
||||||
get publicKey(): PublicKey {
|
get publicKey(): PublicKey {
|
||||||
return new PublicKey(this._keypair.publicKey);
|
return new PublicKey(this._publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The **unencrypted** secret key for this account
|
* The **unencrypted** secret key for this account. The first 32 bytes
|
||||||
|
* is the private scalar and the last 32 bytes is the public key.
|
||||||
|
* Read more: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
|
||||||
*/
|
*/
|
||||||
get secretKey(): Buffer {
|
get secretKey(): Buffer {
|
||||||
return toBuffer(this._keypair.secretKey);
|
return Buffer.concat([this._secretKey, this._publicKey], 64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import nacl from 'tweetnacl';
|
import {generateKeypair, getPublicKey, Ed25519Keypair} from './utils/ed25519';
|
||||||
|
|
||||||
import {PublicKey} from './publickey';
|
import {PublicKey} from './publickey';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,14 +9,6 @@ export interface Signer {
|
||||||
secretKey: Uint8Array;
|
secretKey: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ed25519 Keypair
|
|
||||||
*/
|
|
||||||
export interface Ed25519Keypair {
|
|
||||||
publicKey: Uint8Array;
|
|
||||||
secretKey: Uint8Array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An account keypair used for signing transactions.
|
* An account keypair used for signing transactions.
|
||||||
*/
|
*/
|
||||||
|
@ -31,18 +22,14 @@ export class Keypair {
|
||||||
* @param keypair ed25519 keypair
|
* @param keypair ed25519 keypair
|
||||||
*/
|
*/
|
||||||
constructor(keypair?: Ed25519Keypair) {
|
constructor(keypair?: Ed25519Keypair) {
|
||||||
if (keypair) {
|
this._keypair = keypair ?? generateKeypair();
|
||||||
this._keypair = keypair;
|
|
||||||
} else {
|
|
||||||
this._keypair = nacl.sign.keyPair();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new random keypair
|
* Generate a new random keypair
|
||||||
*/
|
*/
|
||||||
static generate(): Keypair {
|
static generate(): Keypair {
|
||||||
return new Keypair(nacl.sign.keyPair());
|
return new Keypair(generateKeypair());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,16 +48,20 @@ export class Keypair {
|
||||||
secretKey: Uint8Array,
|
secretKey: Uint8Array,
|
||||||
options?: {skipValidation?: boolean},
|
options?: {skipValidation?: boolean},
|
||||||
): Keypair {
|
): Keypair {
|
||||||
const keypair = nacl.sign.keyPair.fromSecretKey(secretKey);
|
if (secretKey.byteLength !== 64) {
|
||||||
|
throw new Error('bad secret key size');
|
||||||
|
}
|
||||||
|
const publicKey = secretKey.slice(32, 64);
|
||||||
if (!options || !options.skipValidation) {
|
if (!options || !options.skipValidation) {
|
||||||
const encoder = new TextEncoder();
|
const privateScalar = secretKey.slice(0, 32);
|
||||||
const signData = encoder.encode('@solana/web3.js-validation-v1');
|
const computedPublicKey = getPublicKey(privateScalar);
|
||||||
const signature = nacl.sign.detached(signData, keypair.secretKey);
|
for (let ii = 0; ii < 32; ii++) {
|
||||||
if (!nacl.sign.detached.verify(signData, signature, keypair.publicKey)) {
|
if (publicKey[ii] !== computedPublicKey[ii]) {
|
||||||
throw new Error('provided secretKey is invalid');
|
throw new Error('provided secretKey is invalid');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Keypair(keypair);
|
return new Keypair({publicKey, secretKey});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,7 +70,11 @@ export class Keypair {
|
||||||
* @param seed seed byte array
|
* @param seed seed byte array
|
||||||
*/
|
*/
|
||||||
static fromSeed(seed: Uint8Array): Keypair {
|
static fromSeed(seed: Uint8Array): Keypair {
|
||||||
return new Keypair(nacl.sign.keyPair.fromSeed(seed));
|
const publicKey = getPublicKey(seed);
|
||||||
|
const secretKey = new Uint8Array(64);
|
||||||
|
secretKey.set(seed);
|
||||||
|
secretKey.set(publicKey, 32);
|
||||||
|
return new Keypair({publicKey, secretKey});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import {Buffer} from 'buffer';
|
import {Buffer} from 'buffer';
|
||||||
import * as BufferLayout from '@solana/buffer-layout';
|
import * as BufferLayout from '@solana/buffer-layout';
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
|
|
||||||
import {Keypair} from '../keypair';
|
import {Keypair} from '../keypair';
|
||||||
import {PublicKey} from '../publickey';
|
import {PublicKey} from '../publickey';
|
||||||
import {TransactionInstruction} from '../transaction';
|
import {TransactionInstruction} from '../transaction';
|
||||||
import assert from '../utils/assert';
|
import assert from '../utils/assert';
|
||||||
|
import {sign} from '../utils/ed25519';
|
||||||
|
|
||||||
const PRIVATE_KEY_BYTES = 64;
|
const PRIVATE_KEY_BYTES = 64;
|
||||||
const PUBLIC_KEY_BYTES = 32;
|
const PUBLIC_KEY_BYTES = 32;
|
||||||
|
@ -142,7 +142,7 @@ export class Ed25519Program {
|
||||||
try {
|
try {
|
||||||
const keypair = Keypair.fromSecretKey(privateKey);
|
const keypair = Keypair.fromSecretKey(privateKey);
|
||||||
const publicKey = keypair.publicKey.toBytes();
|
const publicKey = keypair.publicKey.toBytes();
|
||||||
const signature = nacl.sign.detached(message, keypair.secretKey);
|
const signature = sign(message, keypair.secretKey);
|
||||||
|
|
||||||
return this.createInstructionWithPublicKey({
|
return this.createInstructionWithPublicKey({
|
||||||
publicKey,
|
publicKey,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
import {Buffer} from 'buffer';
|
import {Buffer} from 'buffer';
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
import {sha256} from '@noble/hashes/sha256';
|
import {sha256} from '@noble/hashes/sha256';
|
||||||
|
|
||||||
|
import {isOnCurve} from './utils/ed25519';
|
||||||
import {Struct, SOLANA_SCHEMA} from './utils/borsh-schema';
|
import {Struct, SOLANA_SCHEMA} from './utils/borsh-schema';
|
||||||
import {toBuffer} from './utils/to-buffer';
|
import {toBuffer} from './utils/to-buffer';
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ export class PublicKey extends Struct {
|
||||||
Buffer.from('ProgramDerivedAddress'),
|
Buffer.from('ProgramDerivedAddress'),
|
||||||
]);
|
]);
|
||||||
const publicKeyBytes = sha256(buffer);
|
const publicKeyBytes = sha256(buffer);
|
||||||
if (is_on_curve(publicKeyBytes)) {
|
if (isOnCurve(publicKeyBytes)) {
|
||||||
throw new Error(`Invalid seeds, address must fall off the curve`);
|
throw new Error(`Invalid seeds, address must fall off the curve`);
|
||||||
}
|
}
|
||||||
return new PublicKey(publicKeyBytes);
|
return new PublicKey(publicKeyBytes);
|
||||||
|
@ -228,7 +228,7 @@ export class PublicKey extends Struct {
|
||||||
*/
|
*/
|
||||||
static isOnCurve(pubkeyData: PublicKeyInitData): boolean {
|
static isOnCurve(pubkeyData: PublicKeyInitData): boolean {
|
||||||
const pubkey = new PublicKey(pubkeyData);
|
const pubkey = new PublicKey(pubkeyData);
|
||||||
return is_on_curve(pubkey.toBytes()) == 1;
|
return isOnCurve(pubkey.toBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,66 +236,3 @@ SOLANA_SCHEMA.set(PublicKey, {
|
||||||
kind: 'struct',
|
kind: 'struct',
|
||||||
fields: [['_bn', 'u256']],
|
fields: [['_bn', 'u256']],
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
let naclLowLevel = nacl.lowlevel;
|
|
||||||
|
|
||||||
// Check that a pubkey is on the curve.
|
|
||||||
// This function and its dependents were sourced from:
|
|
||||||
// https://github.com/dchest/tweetnacl-js/blob/f1ec050ceae0861f34280e62498b1d3ed9c350c6/nacl.js#L792
|
|
||||||
function is_on_curve(p: any) {
|
|
||||||
var r = [
|
|
||||||
naclLowLevel.gf(),
|
|
||||||
naclLowLevel.gf(),
|
|
||||||
naclLowLevel.gf(),
|
|
||||||
naclLowLevel.gf(),
|
|
||||||
];
|
|
||||||
|
|
||||||
var t = naclLowLevel.gf(),
|
|
||||||
chk = naclLowLevel.gf(),
|
|
||||||
num = naclLowLevel.gf(),
|
|
||||||
den = naclLowLevel.gf(),
|
|
||||||
den2 = naclLowLevel.gf(),
|
|
||||||
den4 = naclLowLevel.gf(),
|
|
||||||
den6 = naclLowLevel.gf();
|
|
||||||
|
|
||||||
naclLowLevel.set25519(r[2], gf1);
|
|
||||||
naclLowLevel.unpack25519(r[1], p);
|
|
||||||
naclLowLevel.S(num, r[1]);
|
|
||||||
naclLowLevel.M(den, num, naclLowLevel.D);
|
|
||||||
naclLowLevel.Z(num, num, r[2]);
|
|
||||||
naclLowLevel.A(den, r[2], den);
|
|
||||||
|
|
||||||
naclLowLevel.S(den2, den);
|
|
||||||
naclLowLevel.S(den4, den2);
|
|
||||||
naclLowLevel.M(den6, den4, den2);
|
|
||||||
naclLowLevel.M(t, den6, num);
|
|
||||||
naclLowLevel.M(t, t, den);
|
|
||||||
|
|
||||||
naclLowLevel.pow2523(t, t);
|
|
||||||
naclLowLevel.M(t, t, num);
|
|
||||||
naclLowLevel.M(t, t, den);
|
|
||||||
naclLowLevel.M(t, t, den);
|
|
||||||
naclLowLevel.M(r[0], t, den);
|
|
||||||
|
|
||||||
naclLowLevel.S(chk, r[0]);
|
|
||||||
naclLowLevel.M(chk, chk, den);
|
|
||||||
if (neq25519(chk, num)) naclLowLevel.M(r[0], r[0], I);
|
|
||||||
|
|
||||||
naclLowLevel.S(chk, r[0]);
|
|
||||||
naclLowLevel.M(chk, chk, den);
|
|
||||||
if (neq25519(chk, num)) return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
let gf1 = naclLowLevel.gf([1]);
|
|
||||||
let I = naclLowLevel.gf([
|
|
||||||
0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7,
|
|
||||||
0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
|
|
||||||
]);
|
|
||||||
function neq25519(a: any, b: any) {
|
|
||||||
var c = new Uint8Array(32),
|
|
||||||
d = new Uint8Array(32);
|
|
||||||
naclLowLevel.pack25519(c, a);
|
|
||||||
naclLowLevel.pack25519(d, b);
|
|
||||||
return naclLowLevel.crypto_verify_32(c, 0, d, 0);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
import {Buffer} from 'buffer';
|
import {Buffer} from 'buffer';
|
||||||
|
|
||||||
|
@ -12,6 +11,7 @@ import invariant from '../utils/assert';
|
||||||
import type {Signer} from '../keypair';
|
import type {Signer} from '../keypair';
|
||||||
import type {Blockhash} from '../blockhash';
|
import type {Blockhash} from '../blockhash';
|
||||||
import type {CompiledInstruction} from '../message';
|
import type {CompiledInstruction} from '../message';
|
||||||
|
import {sign, verify} from '../utils/ed25519';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transaction signature as base-58 encoded string
|
* Transaction signature as base-58 encoded string
|
||||||
|
@ -658,7 +658,7 @@ export class Transaction {
|
||||||
_partialSign(message: Message, ...signers: Array<Signer>) {
|
_partialSign(message: Message, ...signers: Array<Signer>) {
|
||||||
const signData = message.serialize();
|
const signData = message.serialize();
|
||||||
signers.forEach(signer => {
|
signers.forEach(signer => {
|
||||||
const signature = nacl.sign.detached(signData, signer.secretKey);
|
const signature = sign(signData, signer.secretKey);
|
||||||
this._addSignature(signer.publicKey, toBuffer(signature));
|
this._addSignature(signer.publicKey, toBuffer(signature));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -706,9 +706,7 @@ export class Transaction {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (
|
if (!verify(signature, signData, publicKey.toBuffer())) {
|
||||||
!nacl.sign.detached.verify(signData, signature, publicKey.toBuffer())
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
import * as BufferLayout from '@solana/buffer-layout';
|
import * as BufferLayout from '@solana/buffer-layout';
|
||||||
|
|
||||||
import {Signer} from '../keypair';
|
import {Signer} from '../keypair';
|
||||||
|
@ -7,6 +6,7 @@ import {VersionedMessage} from '../message/versioned';
|
||||||
import {SIGNATURE_LENGTH_IN_BYTES} from './constants';
|
import {SIGNATURE_LENGTH_IN_BYTES} from './constants';
|
||||||
import * as shortvec from '../utils/shortvec-encoding';
|
import * as shortvec from '../utils/shortvec-encoding';
|
||||||
import * as Layout from '../layout';
|
import * as Layout from '../layout';
|
||||||
|
import {sign} from '../utils/ed25519';
|
||||||
|
|
||||||
export type TransactionVersion = 'legacy' | 0;
|
export type TransactionVersion = 'legacy' | 0;
|
||||||
|
|
||||||
|
@ -99,10 +99,7 @@ export class VersionedTransaction {
|
||||||
signerIndex >= 0,
|
signerIndex >= 0,
|
||||||
`Cannot sign with non signer key ${signer.publicKey.toBase58()}`,
|
`Cannot sign with non signer key ${signer.publicKey.toBase58()}`,
|
||||||
);
|
);
|
||||||
this.signatures[signerIndex] = nacl.sign.detached(
|
this.signatures[signerIndex] = sign(messageData, signer.secretKey);
|
||||||
messageData,
|
|
||||||
signer.secretKey,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import {sha512} from '@noble/hashes/sha512';
|
||||||
|
import * as ed25519 from '@noble/ed25519';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 64 byte secret key, the first 32 bytes of which is the
|
||||||
|
* private scalar and the last 32 bytes is the public key.
|
||||||
|
* Read more: https://blog.mozilla.org/warner/2011/11/29/ed25519-keys/
|
||||||
|
*/
|
||||||
|
type Ed25519SecretKey = Uint8Array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ed25519 Keypair
|
||||||
|
*/
|
||||||
|
export interface Ed25519Keypair {
|
||||||
|
publicKey: Uint8Array;
|
||||||
|
secretKey: Ed25519SecretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
ed25519.utils.sha512Sync = (...m) => sha512(ed25519.utils.concatBytes(...m));
|
||||||
|
|
||||||
|
export const generatePrivateKey = ed25519.utils.randomPrivateKey;
|
||||||
|
export const generateKeypair = (): Ed25519Keypair => {
|
||||||
|
const privateScalar = ed25519.utils.randomPrivateKey();
|
||||||
|
const publicKey = getPublicKey(privateScalar);
|
||||||
|
const secretKey = new Uint8Array(64);
|
||||||
|
secretKey.set(privateScalar);
|
||||||
|
secretKey.set(publicKey, 32);
|
||||||
|
return {
|
||||||
|
publicKey,
|
||||||
|
secretKey,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export const getPublicKey = ed25519.sync.getPublicKey;
|
||||||
|
export function isOnCurve(publicKey: Uint8Array): boolean {
|
||||||
|
try {
|
||||||
|
ed25519.Point.fromHex(publicKey, true /* strict */);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const sign = (
|
||||||
|
message: Parameters<typeof ed25519.sync.sign>[0],
|
||||||
|
secretKey: Ed25519SecretKey,
|
||||||
|
) => ed25519.sync.sign(message, secretKey.slice(0, 32));
|
||||||
|
export const verify = ed25519.sync.verify;
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './borsh-schema';
|
export * from './borsh-schema';
|
||||||
export * from './cluster';
|
export * from './cluster';
|
||||||
|
export type {Ed25519Keypair} from './ed25519';
|
||||||
export * from './send-and-confirm-raw-transaction';
|
export * from './send-and-confirm-raw-transaction';
|
||||||
export * from './send-and-confirm-transaction';
|
export * from './send-and-confirm-transaction';
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {Buffer} from 'buffer';
|
import {Buffer} from 'buffer';
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
|
@ -9,6 +8,7 @@ import {
|
||||||
Transaction,
|
Transaction,
|
||||||
Ed25519Program,
|
Ed25519Program,
|
||||||
} from '../../src';
|
} from '../../src';
|
||||||
|
import {sign} from '../../src/utils/ed25519';
|
||||||
import {url} from '../url';
|
import {url} from '../url';
|
||||||
|
|
||||||
if (process.env.TEST_LIVE) {
|
if (process.env.TEST_LIVE) {
|
||||||
|
@ -27,7 +27,7 @@ if (process.env.TEST_LIVE) {
|
||||||
|
|
||||||
it('create ed25519 instruction', async () => {
|
it('create ed25519 instruction', async () => {
|
||||||
const message = Buffer.from('string address');
|
const message = Buffer.from('string address');
|
||||||
const signature = nacl.sign.detached(message, privateKey);
|
const signature = sign(message, privateKey);
|
||||||
const transaction = new Transaction().add(
|
const transaction = new Transaction().add(
|
||||||
Ed25519Program.createInstructionWithPublicKey({
|
Ed25519Program.createInstructionWithPublicKey({
|
||||||
publicKey,
|
publicKey,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
import {Buffer} from 'buffer';
|
import {Buffer} from 'buffer';
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
|
|
||||||
import {Connection} from '../src/connection';
|
import {Connection} from '../src/connection';
|
||||||
|
@ -17,6 +16,7 @@ import invariant from '../src/utils/assert';
|
||||||
import {toBuffer} from '../src/utils/to-buffer';
|
import {toBuffer} from '../src/utils/to-buffer';
|
||||||
import {helpers} from './mocks/rpc-http';
|
import {helpers} from './mocks/rpc-http';
|
||||||
import {url} from './url';
|
import {url} from './url';
|
||||||
|
import {sign} from '../src/utils/ed25519';
|
||||||
|
|
||||||
describe('Transaction', () => {
|
describe('Transaction', () => {
|
||||||
describe('compileMessage', () => {
|
describe('compileMessage', () => {
|
||||||
|
@ -836,7 +836,7 @@ describe('Transaction', () => {
|
||||||
tx.recentBlockhash = bs58.encode(recentBlockhash);
|
tx.recentBlockhash = bs58.encode(recentBlockhash);
|
||||||
tx.setSigners(from.publicKey);
|
tx.setSigners(from.publicKey);
|
||||||
const tx_bytes = tx.serializeMessage();
|
const tx_bytes = tx.serializeMessage();
|
||||||
const signature = nacl.sign.detached(tx_bytes, from.secretKey);
|
const signature = sign(tx_bytes, from.secretKey);
|
||||||
tx.addSignature(from.publicKey, toBuffer(signature));
|
tx.addSignature(from.publicKey, toBuffer(signature));
|
||||||
expect(tx.verifySignatures()).to.be.true;
|
expect(tx.verifySignatures()).to.be.true;
|
||||||
});
|
});
|
||||||
|
@ -855,7 +855,7 @@ describe('Transaction', () => {
|
||||||
tx.recentBlockhash = bs58.encode(recentBlockhash);
|
tx.recentBlockhash = bs58.encode(recentBlockhash);
|
||||||
tx.feePayer = from.publicKey;
|
tx.feePayer = from.publicKey;
|
||||||
const tx_bytes = tx.serializeMessage();
|
const tx_bytes = tx.serializeMessage();
|
||||||
const signature = nacl.sign.detached(tx_bytes, from.secretKey);
|
const signature = sign(tx_bytes, from.secretKey);
|
||||||
tx.addSignature(from.publicKey, toBuffer(signature));
|
tx.addSignature(from.publicKey, toBuffer(signature));
|
||||||
expect(tx.verifySignatures()).to.be.true;
|
expect(tx.verifySignatures()).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import {Buffer} from 'buffer';
|
import {Buffer} from 'buffer';
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import nacl from 'tweetnacl';
|
|
||||||
|
|
||||||
|
import {Keypair} from '../src/keypair';
|
||||||
import {PublicKey} from '../src/publickey';
|
import {PublicKey} from '../src/publickey';
|
||||||
import {ValidatorInfo} from '../src/validator-info';
|
import {ValidatorInfo} from '../src/validator-info';
|
||||||
|
|
||||||
describe('ValidatorInfo', () => {
|
describe('ValidatorInfo', () => {
|
||||||
it('from config account data', () => {
|
it('from config account data', () => {
|
||||||
const keypair = nacl.sign.keyPair.fromSeed(
|
const keypair = Keypair.fromSeed(Uint8Array.from(Array(32).fill(8)));
|
||||||
Uint8Array.from(Array(32).fill(8)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const expectedValidatorInfo = new ValidatorInfo(
|
const expectedValidatorInfo = new ValidatorInfo(
|
||||||
new PublicKey(keypair.publicKey),
|
new PublicKey(keypair.publicKey),
|
||||||
|
|
|
@ -1345,6 +1345,11 @@
|
||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@noble/ed25519@^1.7.0":
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.0.tgz"
|
||||||
|
integrity sha512-LeAxFK0+181zQOhOUuKE8Jnd3duzYhDNd3iCLxpmzA5K+e4I1FdbrK3Ot0ZHBwZMeRD/6EojyUfTbpHZ+hkQHg==
|
||||||
|
|
||||||
"@noble/hashes@^1.1.2":
|
"@noble/hashes@^1.1.2":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz"
|
||||||
|
@ -8215,7 +8220,7 @@ tsutils@^3.21.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.8.1"
|
tslib "^1.8.1"
|
||||||
|
|
||||||
tweetnacl@^1.0.0, tweetnacl@^1.0.3:
|
tweetnacl@^1.0.0:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz"
|
resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz"
|
||||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||||
|
|
Loading…
Reference in New Issue