@solana/web3.js 1.53.0 → 1.55.0
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/lib/index.browser.cjs.js +459 -1825
- package/lib/index.browser.cjs.js.map +1 -1
- package/lib/index.browser.esm.js +455 -1826
- package/lib/index.browser.esm.js.map +1 -1
- package/lib/index.cjs.js +462 -1847
- package/lib/index.cjs.js.map +1 -1
- package/lib/index.d.ts +114 -12
- package/lib/index.esm.js +458 -1848
- package/lib/index.esm.js.map +1 -1
- package/lib/index.iife.js +19272 -25771
- package/lib/index.iife.js.map +1 -1
- package/lib/index.iife.min.js +8 -5
- package/lib/index.iife.min.js.map +1 -1
- package/lib/index.native.js +459 -1824
- package/lib/index.native.js.map +1 -1
- package/package.json +5 -7
- package/src/account.ts +18 -9
- package/src/connection.ts +11 -9
- package/src/keypair.ts +19 -24
- package/src/layout.ts +7 -0
- package/src/message/index.ts +19 -6
- package/src/message/legacy.ts +58 -9
- package/src/message/v0.ts +324 -0
- package/src/message/versioned.ts +36 -0
- package/src/programs/ed25519.ts +2 -2
- package/src/programs/secp256k1.ts +6 -5
- package/src/programs/vote.ts +21 -0
- package/src/publickey.ts +14 -73
- package/src/transaction/constants.ts +2 -0
- package/src/transaction/index.ts +1 -0
- package/src/transaction/legacy.ts +3 -5
- package/src/transaction/versioned.ts +105 -0
- package/src/utils/ed25519.ts +46 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/makeWebsocketUrl.ts +22 -16
- package/src/utils/secp256k1.ts +18 -0
- package/src/validator-info.ts +3 -5
- package/src/utils/__forks__/react-native/url-impl.ts +0 -2
- package/src/utils/url-impl.ts +0 -2
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import bs58 from 'bs58';
|
|
2
|
+
import * as BufferLayout from '@solana/buffer-layout';
|
|
3
|
+
|
|
4
|
+
import * as Layout from '../layout';
|
|
5
|
+
import {Blockhash} from '../blockhash';
|
|
6
|
+
import {
|
|
7
|
+
MessageHeader,
|
|
8
|
+
MessageAddressTableLookup,
|
|
9
|
+
MessageCompiledInstruction,
|
|
10
|
+
} from './index';
|
|
11
|
+
import {PublicKey, PUBLIC_KEY_LENGTH} from '../publickey';
|
|
12
|
+
import * as shortvec from '../utils/shortvec-encoding';
|
|
13
|
+
import assert from '../utils/assert';
|
|
14
|
+
import {PACKET_DATA_SIZE, VERSION_PREFIX_MASK} from '../transaction/constants';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Message constructor arguments
|
|
18
|
+
*/
|
|
19
|
+
export type MessageV0Args = {
|
|
20
|
+
/** The message header, identifying signed and read-only `accountKeys` */
|
|
21
|
+
header: MessageHeader;
|
|
22
|
+
/** The static account keys used by this transaction */
|
|
23
|
+
staticAccountKeys: PublicKey[];
|
|
24
|
+
/** The hash of a recent ledger block */
|
|
25
|
+
recentBlockhash: Blockhash;
|
|
26
|
+
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
|
|
27
|
+
compiledInstructions: MessageCompiledInstruction[];
|
|
28
|
+
/** Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */
|
|
29
|
+
addressTableLookups: MessageAddressTableLookup[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export class MessageV0 {
|
|
33
|
+
header: MessageHeader;
|
|
34
|
+
staticAccountKeys: Array<PublicKey>;
|
|
35
|
+
recentBlockhash: Blockhash;
|
|
36
|
+
compiledInstructions: Array<MessageCompiledInstruction>;
|
|
37
|
+
addressTableLookups: Array<MessageAddressTableLookup>;
|
|
38
|
+
|
|
39
|
+
constructor(args: MessageV0Args) {
|
|
40
|
+
this.header = args.header;
|
|
41
|
+
this.staticAccountKeys = args.staticAccountKeys;
|
|
42
|
+
this.recentBlockhash = args.recentBlockhash;
|
|
43
|
+
this.compiledInstructions = args.compiledInstructions;
|
|
44
|
+
this.addressTableLookups = args.addressTableLookups;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get version(): 0 {
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
serialize(): Uint8Array {
|
|
52
|
+
const encodedStaticAccountKeysLength = Array<number>();
|
|
53
|
+
shortvec.encodeLength(
|
|
54
|
+
encodedStaticAccountKeysLength,
|
|
55
|
+
this.staticAccountKeys.length,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const serializedInstructions = this.serializeInstructions();
|
|
59
|
+
const encodedInstructionsLength = Array<number>();
|
|
60
|
+
shortvec.encodeLength(
|
|
61
|
+
encodedInstructionsLength,
|
|
62
|
+
this.compiledInstructions.length,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const serializedAddressTableLookups = this.serializeAddressTableLookups();
|
|
66
|
+
const encodedAddressTableLookupsLength = Array<number>();
|
|
67
|
+
shortvec.encodeLength(
|
|
68
|
+
encodedAddressTableLookupsLength,
|
|
69
|
+
this.addressTableLookups.length,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const messageLayout = BufferLayout.struct<{
|
|
73
|
+
prefix: number;
|
|
74
|
+
header: MessageHeader;
|
|
75
|
+
staticAccountKeysLength: Uint8Array;
|
|
76
|
+
staticAccountKeys: Array<Uint8Array>;
|
|
77
|
+
recentBlockhash: Uint8Array;
|
|
78
|
+
instructionsLength: Uint8Array;
|
|
79
|
+
serializedInstructions: Uint8Array;
|
|
80
|
+
addressTableLookupsLength: Uint8Array;
|
|
81
|
+
serializedAddressTableLookups: Uint8Array;
|
|
82
|
+
}>([
|
|
83
|
+
BufferLayout.u8('prefix'),
|
|
84
|
+
BufferLayout.struct<MessageHeader>(
|
|
85
|
+
[
|
|
86
|
+
BufferLayout.u8('numRequiredSignatures'),
|
|
87
|
+
BufferLayout.u8('numReadonlySignedAccounts'),
|
|
88
|
+
BufferLayout.u8('numReadonlyUnsignedAccounts'),
|
|
89
|
+
],
|
|
90
|
+
'header',
|
|
91
|
+
),
|
|
92
|
+
BufferLayout.blob(
|
|
93
|
+
encodedStaticAccountKeysLength.length,
|
|
94
|
+
'staticAccountKeysLength',
|
|
95
|
+
),
|
|
96
|
+
BufferLayout.seq(
|
|
97
|
+
Layout.publicKey(),
|
|
98
|
+
this.staticAccountKeys.length,
|
|
99
|
+
'staticAccountKeys',
|
|
100
|
+
),
|
|
101
|
+
Layout.publicKey('recentBlockhash'),
|
|
102
|
+
BufferLayout.blob(encodedInstructionsLength.length, 'instructionsLength'),
|
|
103
|
+
BufferLayout.blob(
|
|
104
|
+
serializedInstructions.length,
|
|
105
|
+
'serializedInstructions',
|
|
106
|
+
),
|
|
107
|
+
BufferLayout.blob(
|
|
108
|
+
encodedAddressTableLookupsLength.length,
|
|
109
|
+
'addressTableLookupsLength',
|
|
110
|
+
),
|
|
111
|
+
BufferLayout.blob(
|
|
112
|
+
serializedAddressTableLookups.length,
|
|
113
|
+
'serializedAddressTableLookups',
|
|
114
|
+
),
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
const serializedMessage = new Uint8Array(PACKET_DATA_SIZE);
|
|
118
|
+
const MESSAGE_VERSION_0_PREFIX = 1 << 7;
|
|
119
|
+
const serializedMessageLength = messageLayout.encode(
|
|
120
|
+
{
|
|
121
|
+
prefix: MESSAGE_VERSION_0_PREFIX,
|
|
122
|
+
header: this.header,
|
|
123
|
+
staticAccountKeysLength: new Uint8Array(encodedStaticAccountKeysLength),
|
|
124
|
+
staticAccountKeys: this.staticAccountKeys.map(key => key.toBytes()),
|
|
125
|
+
recentBlockhash: bs58.decode(this.recentBlockhash),
|
|
126
|
+
instructionsLength: new Uint8Array(encodedInstructionsLength),
|
|
127
|
+
serializedInstructions,
|
|
128
|
+
addressTableLookupsLength: new Uint8Array(
|
|
129
|
+
encodedAddressTableLookupsLength,
|
|
130
|
+
),
|
|
131
|
+
serializedAddressTableLookups,
|
|
132
|
+
},
|
|
133
|
+
serializedMessage,
|
|
134
|
+
);
|
|
135
|
+
return serializedMessage.slice(0, serializedMessageLength);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private serializeInstructions(): Uint8Array {
|
|
139
|
+
let serializedLength = 0;
|
|
140
|
+
const serializedInstructions = new Uint8Array(PACKET_DATA_SIZE);
|
|
141
|
+
for (const instruction of this.compiledInstructions) {
|
|
142
|
+
const encodedAccountKeyIndexesLength = Array<number>();
|
|
143
|
+
shortvec.encodeLength(
|
|
144
|
+
encodedAccountKeyIndexesLength,
|
|
145
|
+
instruction.accountKeyIndexes.length,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const encodedDataLength = Array<number>();
|
|
149
|
+
shortvec.encodeLength(encodedDataLength, instruction.data.length);
|
|
150
|
+
|
|
151
|
+
const instructionLayout = BufferLayout.struct<{
|
|
152
|
+
programIdIndex: number;
|
|
153
|
+
encodedAccountKeyIndexesLength: Uint8Array;
|
|
154
|
+
accountKeyIndexes: number[];
|
|
155
|
+
encodedDataLength: Uint8Array;
|
|
156
|
+
data: Uint8Array;
|
|
157
|
+
}>([
|
|
158
|
+
BufferLayout.u8('programIdIndex'),
|
|
159
|
+
BufferLayout.blob(
|
|
160
|
+
encodedAccountKeyIndexesLength.length,
|
|
161
|
+
'encodedAccountKeyIndexesLength',
|
|
162
|
+
),
|
|
163
|
+
BufferLayout.seq(
|
|
164
|
+
BufferLayout.u8(),
|
|
165
|
+
instruction.accountKeyIndexes.length,
|
|
166
|
+
'accountKeyIndexes',
|
|
167
|
+
),
|
|
168
|
+
BufferLayout.blob(encodedDataLength.length, 'encodedDataLength'),
|
|
169
|
+
BufferLayout.blob(instruction.data.length, 'data'),
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
serializedLength += instructionLayout.encode(
|
|
173
|
+
{
|
|
174
|
+
programIdIndex: instruction.programIdIndex,
|
|
175
|
+
encodedAccountKeyIndexesLength: new Uint8Array(
|
|
176
|
+
encodedAccountKeyIndexesLength,
|
|
177
|
+
),
|
|
178
|
+
accountKeyIndexes: instruction.accountKeyIndexes,
|
|
179
|
+
encodedDataLength: new Uint8Array(encodedDataLength),
|
|
180
|
+
data: instruction.data,
|
|
181
|
+
},
|
|
182
|
+
serializedInstructions,
|
|
183
|
+
serializedLength,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return serializedInstructions.slice(0, serializedLength);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private serializeAddressTableLookups(): Uint8Array {
|
|
191
|
+
let serializedLength = 0;
|
|
192
|
+
const serializedAddressTableLookups = new Uint8Array(PACKET_DATA_SIZE);
|
|
193
|
+
for (const lookup of this.addressTableLookups) {
|
|
194
|
+
const encodedWritableIndexesLength = Array<number>();
|
|
195
|
+
shortvec.encodeLength(
|
|
196
|
+
encodedWritableIndexesLength,
|
|
197
|
+
lookup.writableIndexes.length,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const encodedReadonlyIndexesLength = Array<number>();
|
|
201
|
+
shortvec.encodeLength(
|
|
202
|
+
encodedReadonlyIndexesLength,
|
|
203
|
+
lookup.readonlyIndexes.length,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const addressTableLookupLayout = BufferLayout.struct<{
|
|
207
|
+
accountKey: Uint8Array;
|
|
208
|
+
encodedWritableIndexesLength: Uint8Array;
|
|
209
|
+
writableIndexes: number[];
|
|
210
|
+
encodedReadonlyIndexesLength: Uint8Array;
|
|
211
|
+
readonlyIndexes: number[];
|
|
212
|
+
}>([
|
|
213
|
+
Layout.publicKey('accountKey'),
|
|
214
|
+
BufferLayout.blob(
|
|
215
|
+
encodedWritableIndexesLength.length,
|
|
216
|
+
'encodedWritableIndexesLength',
|
|
217
|
+
),
|
|
218
|
+
BufferLayout.seq(
|
|
219
|
+
BufferLayout.u8(),
|
|
220
|
+
lookup.writableIndexes.length,
|
|
221
|
+
'writableIndexes',
|
|
222
|
+
),
|
|
223
|
+
BufferLayout.blob(
|
|
224
|
+
encodedReadonlyIndexesLength.length,
|
|
225
|
+
'encodedReadonlyIndexesLength',
|
|
226
|
+
),
|
|
227
|
+
BufferLayout.seq(
|
|
228
|
+
BufferLayout.u8(),
|
|
229
|
+
lookup.readonlyIndexes.length,
|
|
230
|
+
'readonlyIndexes',
|
|
231
|
+
),
|
|
232
|
+
]);
|
|
233
|
+
|
|
234
|
+
serializedLength += addressTableLookupLayout.encode(
|
|
235
|
+
{
|
|
236
|
+
accountKey: lookup.accountKey.toBytes(),
|
|
237
|
+
encodedWritableIndexesLength: new Uint8Array(
|
|
238
|
+
encodedWritableIndexesLength,
|
|
239
|
+
),
|
|
240
|
+
writableIndexes: lookup.writableIndexes,
|
|
241
|
+
encodedReadonlyIndexesLength: new Uint8Array(
|
|
242
|
+
encodedReadonlyIndexesLength,
|
|
243
|
+
),
|
|
244
|
+
readonlyIndexes: lookup.readonlyIndexes,
|
|
245
|
+
},
|
|
246
|
+
serializedAddressTableLookups,
|
|
247
|
+
serializedLength,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return serializedAddressTableLookups.slice(0, serializedLength);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static deserialize(serializedMessage: Uint8Array): MessageV0 {
|
|
255
|
+
let byteArray = [...serializedMessage];
|
|
256
|
+
|
|
257
|
+
const prefix = byteArray.shift() as number;
|
|
258
|
+
const maskedPrefix = prefix & VERSION_PREFIX_MASK;
|
|
259
|
+
assert(
|
|
260
|
+
prefix !== maskedPrefix,
|
|
261
|
+
`Expected versioned message but received legacy message`,
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const version = maskedPrefix;
|
|
265
|
+
assert(
|
|
266
|
+
version === 0,
|
|
267
|
+
`Expected versioned message with version 0 but found version ${version}`,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const header: MessageHeader = {
|
|
271
|
+
numRequiredSignatures: byteArray.shift() as number,
|
|
272
|
+
numReadonlySignedAccounts: byteArray.shift() as number,
|
|
273
|
+
numReadonlyUnsignedAccounts: byteArray.shift() as number,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const staticAccountKeys = [];
|
|
277
|
+
const staticAccountKeysLength = shortvec.decodeLength(byteArray);
|
|
278
|
+
for (let i = 0; i < staticAccountKeysLength; i++) {
|
|
279
|
+
staticAccountKeys.push(
|
|
280
|
+
new PublicKey(byteArray.splice(0, PUBLIC_KEY_LENGTH)),
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const recentBlockhash = bs58.encode(byteArray.splice(0, PUBLIC_KEY_LENGTH));
|
|
285
|
+
|
|
286
|
+
const instructionCount = shortvec.decodeLength(byteArray);
|
|
287
|
+
const compiledInstructions: MessageCompiledInstruction[] = [];
|
|
288
|
+
for (let i = 0; i < instructionCount; i++) {
|
|
289
|
+
const programIdIndex = byteArray.shift() as number;
|
|
290
|
+
const accountKeyIndexesLength = shortvec.decodeLength(byteArray);
|
|
291
|
+
const accountKeyIndexes = byteArray.splice(0, accountKeyIndexesLength);
|
|
292
|
+
const dataLength = shortvec.decodeLength(byteArray);
|
|
293
|
+
const data = new Uint8Array(byteArray.splice(0, dataLength));
|
|
294
|
+
compiledInstructions.push({
|
|
295
|
+
programIdIndex,
|
|
296
|
+
accountKeyIndexes,
|
|
297
|
+
data,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const addressTableLookupsCount = shortvec.decodeLength(byteArray);
|
|
302
|
+
const addressTableLookups: MessageAddressTableLookup[] = [];
|
|
303
|
+
for (let i = 0; i < addressTableLookupsCount; i++) {
|
|
304
|
+
const accountKey = new PublicKey(byteArray.splice(0, PUBLIC_KEY_LENGTH));
|
|
305
|
+
const writableIndexesLength = shortvec.decodeLength(byteArray);
|
|
306
|
+
const writableIndexes = byteArray.splice(0, writableIndexesLength);
|
|
307
|
+
const readonlyIndexesLength = shortvec.decodeLength(byteArray);
|
|
308
|
+
const readonlyIndexes = byteArray.splice(0, readonlyIndexesLength);
|
|
309
|
+
addressTableLookups.push({
|
|
310
|
+
accountKey,
|
|
311
|
+
writableIndexes,
|
|
312
|
+
readonlyIndexes,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return new MessageV0({
|
|
317
|
+
header,
|
|
318
|
+
staticAccountKeys,
|
|
319
|
+
recentBlockhash,
|
|
320
|
+
compiledInstructions,
|
|
321
|
+
addressTableLookups,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {VERSION_PREFIX_MASK} from '../transaction/constants';
|
|
2
|
+
import {Message} from './legacy';
|
|
3
|
+
import {MessageV0} from './v0';
|
|
4
|
+
|
|
5
|
+
export type VersionedMessage = Message | MessageV0;
|
|
6
|
+
// eslint-disable-next-line no-redeclare
|
|
7
|
+
export const VersionedMessage = {
|
|
8
|
+
deserializeMessageVersion(serializedMessage: Uint8Array): 'legacy' | number {
|
|
9
|
+
const prefix = serializedMessage[0];
|
|
10
|
+
const maskedPrefix = prefix & VERSION_PREFIX_MASK;
|
|
11
|
+
|
|
12
|
+
// if the highest bit of the prefix is not set, the message is not versioned
|
|
13
|
+
if (maskedPrefix === prefix) {
|
|
14
|
+
return 'legacy';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// the lower 7 bits of the prefix indicate the message version
|
|
18
|
+
return maskedPrefix;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
deserialize: (serializedMessage: Uint8Array): VersionedMessage => {
|
|
22
|
+
const version =
|
|
23
|
+
VersionedMessage.deserializeMessageVersion(serializedMessage);
|
|
24
|
+
if (version === 'legacy') {
|
|
25
|
+
return Message.from(serializedMessage);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (version === 0) {
|
|
29
|
+
return MessageV0.deserialize(serializedMessage);
|
|
30
|
+
} else {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Transaction message version ${version} deserialization is not supported`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
package/src/programs/ed25519.ts
CHANGED
|
@@ -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 =
|
|
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(
|
|
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
|
|
217
|
+
const [signature, recoveryId] = ecdsaSign(messageHash, privateKey);
|
|
217
218
|
|
|
218
219
|
return this.createInstructionWithPublicKey({
|
|
219
220
|
publicKey,
|
package/src/programs/vote.ts
CHANGED
|
@@ -410,4 +410,25 @@ export class VoteProgram {
|
|
|
410
410
|
data,
|
|
411
411
|
});
|
|
412
412
|
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Generate a transaction to withdraw safely from a Vote account.
|
|
416
|
+
*
|
|
417
|
+
* This function was created as a safeguard for vote accounts running validators, `safeWithdraw`
|
|
418
|
+
* checks that the withdraw amount will not exceed the specified balance while leaving enough left
|
|
419
|
+
* to cover rent. If you wish to close the vote account by withdrawing the full amount, call the
|
|
420
|
+
* `withdraw` method directly.
|
|
421
|
+
*/
|
|
422
|
+
static safeWithdraw(
|
|
423
|
+
params: WithdrawFromVoteAccountParams,
|
|
424
|
+
currentVoteAccountBalance: number,
|
|
425
|
+
rentExemptMinimum: number,
|
|
426
|
+
): Transaction {
|
|
427
|
+
if (params.lamports > currentVoteAccountBalance - rentExemptMinimum) {
|
|
428
|
+
throw new Error(
|
|
429
|
+
'Withdraw will leave vote account with insuffcient funds.',
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
return VoteProgram.withdraw(params);
|
|
433
|
+
}
|
|
413
434
|
}
|
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
|
|
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
|
|
|
@@ -12,6 +12,11 @@ import {toBuffer} from './utils/to-buffer';
|
|
|
12
12
|
*/
|
|
13
13
|
export const MAX_SEED_LENGTH = 32;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Size of public key in bytes
|
|
17
|
+
*/
|
|
18
|
+
export const PUBLIC_KEY_LENGTH = 32;
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Value to be converted into public key
|
|
17
22
|
*/
|
|
@@ -54,7 +59,7 @@ export class PublicKey extends Struct {
|
|
|
54
59
|
if (typeof value === 'string') {
|
|
55
60
|
// assume base 58 encoding by default
|
|
56
61
|
const decoded = bs58.decode(value);
|
|
57
|
-
if (decoded.length !=
|
|
62
|
+
if (decoded.length != PUBLIC_KEY_LENGTH) {
|
|
58
63
|
throw new Error(`Invalid public key input`);
|
|
59
64
|
}
|
|
60
65
|
this._bn = new BN(decoded);
|
|
@@ -103,7 +108,7 @@ export class PublicKey extends Struct {
|
|
|
103
108
|
*/
|
|
104
109
|
toBuffer(): Buffer {
|
|
105
110
|
const b = this._bn.toArrayLike(Buffer);
|
|
106
|
-
if (b.length ===
|
|
111
|
+
if (b.length === PUBLIC_KEY_LENGTH) {
|
|
107
112
|
return b;
|
|
108
113
|
}
|
|
109
114
|
|
|
@@ -135,8 +140,8 @@ export class PublicKey extends Struct {
|
|
|
135
140
|
Buffer.from(seed),
|
|
136
141
|
programId.toBuffer(),
|
|
137
142
|
]);
|
|
138
|
-
const
|
|
139
|
-
return new PublicKey(
|
|
143
|
+
const publicKeyBytes = sha256(buffer);
|
|
144
|
+
return new PublicKey(publicKeyBytes);
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
/**
|
|
@@ -159,9 +164,8 @@ export class PublicKey extends Struct {
|
|
|
159
164
|
programId.toBuffer(),
|
|
160
165
|
Buffer.from('ProgramDerivedAddress'),
|
|
161
166
|
]);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (is_on_curve(publicKeyBytes)) {
|
|
167
|
+
const publicKeyBytes = sha256(buffer);
|
|
168
|
+
if (isOnCurve(publicKeyBytes)) {
|
|
165
169
|
throw new Error(`Invalid seeds, address must fall off the curve`);
|
|
166
170
|
}
|
|
167
171
|
return new PublicKey(publicKeyBytes);
|
|
@@ -224,7 +228,7 @@ export class PublicKey extends Struct {
|
|
|
224
228
|
*/
|
|
225
229
|
static isOnCurve(pubkeyData: PublicKeyInitData): boolean {
|
|
226
230
|
const pubkey = new PublicKey(pubkeyData);
|
|
227
|
-
return
|
|
231
|
+
return isOnCurve(pubkey.toBytes());
|
|
228
232
|
}
|
|
229
233
|
}
|
|
230
234
|
|
|
@@ -232,66 +236,3 @@ SOLANA_SCHEMA.set(PublicKey, {
|
|
|
232
236
|
kind: 'struct',
|
|
233
237
|
fields: [['_bn', 'u256']],
|
|
234
238
|
});
|
|
235
|
-
|
|
236
|
-
// @ts-ignore
|
|
237
|
-
let naclLowLevel = nacl.lowlevel;
|
|
238
|
-
|
|
239
|
-
// Check that a pubkey is on the curve.
|
|
240
|
-
// This function and its dependents were sourced from:
|
|
241
|
-
// https://github.com/dchest/tweetnacl-js/blob/f1ec050ceae0861f34280e62498b1d3ed9c350c6/nacl.js#L792
|
|
242
|
-
function is_on_curve(p: any) {
|
|
243
|
-
var r = [
|
|
244
|
-
naclLowLevel.gf(),
|
|
245
|
-
naclLowLevel.gf(),
|
|
246
|
-
naclLowLevel.gf(),
|
|
247
|
-
naclLowLevel.gf(),
|
|
248
|
-
];
|
|
249
|
-
|
|
250
|
-
var t = naclLowLevel.gf(),
|
|
251
|
-
chk = naclLowLevel.gf(),
|
|
252
|
-
num = naclLowLevel.gf(),
|
|
253
|
-
den = naclLowLevel.gf(),
|
|
254
|
-
den2 = naclLowLevel.gf(),
|
|
255
|
-
den4 = naclLowLevel.gf(),
|
|
256
|
-
den6 = naclLowLevel.gf();
|
|
257
|
-
|
|
258
|
-
naclLowLevel.set25519(r[2], gf1);
|
|
259
|
-
naclLowLevel.unpack25519(r[1], p);
|
|
260
|
-
naclLowLevel.S(num, r[1]);
|
|
261
|
-
naclLowLevel.M(den, num, naclLowLevel.D);
|
|
262
|
-
naclLowLevel.Z(num, num, r[2]);
|
|
263
|
-
naclLowLevel.A(den, r[2], den);
|
|
264
|
-
|
|
265
|
-
naclLowLevel.S(den2, den);
|
|
266
|
-
naclLowLevel.S(den4, den2);
|
|
267
|
-
naclLowLevel.M(den6, den4, den2);
|
|
268
|
-
naclLowLevel.M(t, den6, num);
|
|
269
|
-
naclLowLevel.M(t, t, den);
|
|
270
|
-
|
|
271
|
-
naclLowLevel.pow2523(t, t);
|
|
272
|
-
naclLowLevel.M(t, t, num);
|
|
273
|
-
naclLowLevel.M(t, t, den);
|
|
274
|
-
naclLowLevel.M(t, t, den);
|
|
275
|
-
naclLowLevel.M(r[0], t, den);
|
|
276
|
-
|
|
277
|
-
naclLowLevel.S(chk, r[0]);
|
|
278
|
-
naclLowLevel.M(chk, chk, den);
|
|
279
|
-
if (neq25519(chk, num)) naclLowLevel.M(r[0], r[0], I);
|
|
280
|
-
|
|
281
|
-
naclLowLevel.S(chk, r[0]);
|
|
282
|
-
naclLowLevel.M(chk, chk, den);
|
|
283
|
-
if (neq25519(chk, num)) return 0;
|
|
284
|
-
return 1;
|
|
285
|
-
}
|
|
286
|
-
let gf1 = naclLowLevel.gf([1]);
|
|
287
|
-
let I = naclLowLevel.gf([
|
|
288
|
-
0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7,
|
|
289
|
-
0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83,
|
|
290
|
-
]);
|
|
291
|
-
function neq25519(a: any, b: any) {
|
|
292
|
-
var c = new Uint8Array(32),
|
|
293
|
-
d = new Uint8Array(32);
|
|
294
|
-
naclLowLevel.pack25519(c, a);
|
|
295
|
-
naclLowLevel.pack25519(d, b);
|
|
296
|
-
return naclLowLevel.crypto_verify_32(c, 0, d, 0);
|
|
297
|
-
}
|
package/src/transaction/index.ts
CHANGED
|
@@ -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,7 @@ 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
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Transaction signature as base-58 encoded string
|
|
@@ -658,7 +658,7 @@ export class Transaction {
|
|
|
658
658
|
_partialSign(message: Message, ...signers: Array<Signer>) {
|
|
659
659
|
const signData = message.serialize();
|
|
660
660
|
signers.forEach(signer => {
|
|
661
|
-
const signature =
|
|
661
|
+
const signature = sign(signData, signer.secretKey);
|
|
662
662
|
this._addSignature(signer.publicKey, toBuffer(signature));
|
|
663
663
|
});
|
|
664
664
|
}
|
|
@@ -706,9 +706,7 @@ export class Transaction {
|
|
|
706
706
|
return false;
|
|
707
707
|
}
|
|
708
708
|
} else {
|
|
709
|
-
if (
|
|
710
|
-
!nacl.sign.detached.verify(signData, signature, publicKey.toBuffer())
|
|
711
|
-
) {
|
|
709
|
+
if (!verify(signature, signData, publicKey.toBuffer())) {
|
|
712
710
|
return false;
|
|
713
711
|
}
|
|
714
712
|
}
|