Skip to content

Commit d5fbd59

Browse files
committed
⚡ Optimize find program key by adding static buffers and reusing the same SHA256 while finding an off-curve key
1 parent aadb40d commit d5fbd59

File tree

2 files changed

+46
-47
lines changed

2 files changed

+46
-47
lines changed

src/Solana.Unity.Dex/Orca/Address/AddressUtils.cs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,8 @@ public static class AddressUtils
2020
/// <returns>A Pda (program-derived address) off the curve, or null.</returns>
2121
public static Pda FindProgramAddress(IEnumerable<byte[]> seeds, PublicKey programId)
2222
{
23-
byte nonce = 255;
24-
25-
while (nonce != 0)
26-
{
27-
PublicKey address;
28-
List<byte[]> seedsWithNonce = new List<byte[]>(seeds);
29-
seedsWithNonce.Add(new byte[] { nonce });
30-
31-
//try to generate the address
32-
bool created = PublicKey.TryCreateProgramAddress(new List<byte[]>(seedsWithNonce), programId, out address);
33-
34-
//if succeeded, return
35-
if (created)
36-
{
37-
return new Pda(address, nonce);
38-
}
39-
40-
//decrease the nonce and retry if failed
41-
nonce--;
42-
}
43-
44-
return null;
23+
return PublicKey.TryFindProgramAddress(seeds, programId, out PublicKey pubkey, out byte bump)
24+
? new Pda(pubkey, bump) : null;
4525
}
4626

4727
/// <summary>

src/Solana.Unity.Wallet/PublicKey.cs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -256,44 +256,49 @@ public static bool IsValid(ReadOnlySpan<byte> key, bool validateCurve = false)
256256
/// <summary>
257257
/// The bytes of the `ProgramDerivedAddress` string.
258258
/// </summary>
259-
private static readonly byte[] ProgramDerivedAddressBytes = Encoding.UTF8.GetBytes("ProgramDerivedAddress");
259+
private static readonly byte[] ProgramDerivedAddressBytes = "ProgramDerivedAddress"u8.ToArray();
260260

261261
/// <summary>
262262
/// Derives a program address.
263263
/// </summary>
264264
/// <param name="seeds">The address seeds.</param>
265265
/// <param name="programId">The program Id.</param>
266266
/// <param name="publicKey">The derived public key, returned as inline out.</param>
267-
/// <returns>true if it could derive the program address for the given seeds, otherwise false..</returns>
267+
/// <returns>true if it could derive the program address for the given seeds, otherwise false.</returns>
268268
/// <exception cref="ArgumentException">Throws exception when one of the seeds has an invalid length.</exception>
269269
public static bool TryCreateProgramAddress(ICollection<byte[]> seeds, PublicKey programId, out PublicKey publicKey)
270270
{
271-
MemoryStream buffer = new(PublicKeyLength * seeds.Count + ProgramDerivedAddressBytes.Length + programId.KeyBytes.Length);
272-
271+
using SHA256 sha256 = SHA256.Create();
272+
sha256.Initialize();
273+
273274
foreach (byte[] seed in seeds)
274275
{
275276
if (seed.Length > PublicKeyLength)
276277
{
277278
throw new ArgumentException("max seed length exceeded", nameof(seeds));
278279
}
279-
buffer.Write(seed,0, seed.Length);
280+
281+
sha256.TransformBlock(seed,0, seed.Length, null, 0);
280282
}
281283

282-
buffer.Write(programId.KeyBytes, 0, programId.KeyBytes.Length);
283-
buffer.Write(ProgramDerivedAddressBytes, 0, ProgramDerivedAddressBytes.Length);
284-
285-
SHA256 sha256 = SHA256.Create();
286-
byte[] hash = sha256.ComputeHash(new ReadOnlySpan<byte>(buffer.GetBuffer(), 0, (int)buffer.Length).ToArray());
287-
284+
sha256.TransformBlock(programId.KeyBytes,0, programId.KeyBytes.Length, null,0);
285+
sha256.TransformBlock(ProgramDerivedAddressBytes, 0, ProgramDerivedAddressBytes.Length, null, 0);
286+
sha256.TransformFinalBlock([], 0, 0);
287+
288+
byte[] hash = sha256.Hash!;
288289
if (hash.IsOnCurve())
289290
{
290291
publicKey = null;
291292
return false;
292293
}
293-
publicKey = new(hash);
294+
295+
publicKey = new PublicKey(hash);
294296
return true;
295297
}
296298

299+
private static readonly List<byte[]> PdaSeedsBuffer = [];
300+
private static readonly byte[] BumpArray = new byte[1];
301+
297302
/// <summary>
298303
/// Attempts to find a program address for the passed seeds and program Id.
299304
/// </summary>
@@ -304,28 +309,42 @@ public static bool TryCreateProgramAddress(ICollection<byte[]> seeds, PublicKey
304309
/// <returns>True whenever the address for a nonce was found, otherwise false.</returns>
305310
public static bool TryFindProgramAddress(IEnumerable<byte[]> seeds, PublicKey programId, out PublicKey address, out byte bump)
306311
{
307-
byte seedBump = 255;
308-
List<byte[]> buffer = seeds.ToList();
309-
var bumpArray = new byte[1];
310-
buffer.Add(bumpArray);
312+
PdaSeedsBuffer.Clear();
313+
PdaSeedsBuffer.AddRange(seeds);
311314

312-
while (seedBump != 0)
315+
if (PdaSeedsBuffer.Any(seed => seed.Length > PublicKeyLength))
316+
{
317+
throw new ArgumentException("max seed length exceeded", nameof(seeds));
318+
}
319+
320+
using SHA256 sha256 = SHA256.Create();
321+
for (bump = 255; ; bump--)
313322
{
314-
bumpArray[0] = seedBump;
315-
bool success = TryCreateProgramAddress(buffer, programId, out PublicKey derivedAddress);
323+
sha256.Initialize();
316324

317-
if (success)
325+
foreach (byte[] seed in PdaSeedsBuffer)
326+
{
327+
sha256.TransformBlock(seed, 0, seed.Length, null, 0);
328+
}
329+
330+
BumpArray[0] = bump;
331+
sha256.TransformBlock(BumpArray, 0, 1, null, 0);
332+
sha256.TransformBlock(programId.KeyBytes, 0, programId.KeyBytes.Length, null, 0);
333+
sha256.TransformBlock(ProgramDerivedAddressBytes, 0, ProgramDerivedAddressBytes.Length, null, 0);
334+
sha256.TransformFinalBlock([], 0, 0);
335+
336+
byte[] hash = sha256.Hash!;
337+
if (!hash.IsOnCurve())
318338
{
319-
address = derivedAddress;
320-
bump = seedBump;
339+
address = new PublicKey(hash);
321340
return true;
322341
}
323342

324-
seedBump--;
343+
if (bump == 0)
344+
break;
325345
}
326346

327-
address = null;
328-
bump = 0;
347+
address = null!;
329348
return false;
330349
}
331350

0 commit comments

Comments
 (0)