Skip to content

Commit 1a3fa6a

Browse files
committed
concurrent signing + perf improvements
1 parent 828ac78 commit 1a3fa6a

2 files changed

Lines changed: 62 additions & 25 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/google/uuid v1.6.0
1010
github.com/mr-tron/base58 v1.2.0
1111
go.mongodb.org/mongo-driver/v2 v2.5.0
12+
golang.org/x/sync v0.19.0
1213
)
1314

1415
require (

transaction.go

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/gagliardetto/treeout"
3030
"github.com/mr-tron/base58"
3131
"go.uber.org/zap"
32+
"golang.org/x/sync/errgroup"
3233
)
3334

3435
type Transaction struct {
@@ -284,24 +285,25 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
284285
}
285286
}
286287

287-
programIDs := make(PublicKeySlice, 0)
288-
accounts := []*AccountMeta{}
288+
programIDsMap := make(map[PublicKey]struct{}, len(instructions))
289+
290+
total := 0
291+
for _, instruction := range instructions {
292+
total += len(instruction.Accounts())
293+
programIDsMap[instruction.ProgramID()] = struct{}{}
294+
}
295+
accounts := make([]*AccountMeta, 0, total+len(programIDsMap))
296+
289297
for _, instruction := range instructions {
290298
accounts = append(accounts, instruction.Accounts()...)
291-
programIDs.UniqueAppend(instruction.ProgramID())
292299
}
293300

294-
// for IsInvoked check
295-
programIDsMap := make(map[PublicKey]struct{}, len(programIDs))
296-
// Add programID to the account list
297-
for _, programID := range programIDs {
301+
for programID := range programIDsMap {
298302
accounts = append(accounts, &AccountMeta{
299303
PublicKey: programID,
300304
IsSigner: false,
301305
IsWritable: false,
302306
})
303-
304-
programIDsMap[programID] = struct{}{}
305307
}
306308

307309
// Sort. Prioritizing first by signer, then by writable
@@ -315,8 +317,8 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
315317
return 0
316318
})
317319

318-
uniqAccountsMap := map[PublicKey]uint64{}
319-
uniqAccounts := []*AccountMeta{}
320+
uniqAccountsMap := make(map[PublicKey]uint64, len(accounts))
321+
uniqAccounts := make([]*AccountMeta, 0, len(accounts))
320322
for _, acc := range accounts {
321323
if index, found := uniqAccountsMap[acc.PublicKey]; found {
322324
uniqAccounts[index].IsWritable = uniqAccounts[index].IsWritable || acc.IsWritable
@@ -334,6 +336,7 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
334336
for idx, acc := range uniqAccounts {
335337
if acc.PublicKey.Equals(feePayer) {
336338
feePayerIndex = idx
339+
break
337340
}
338341
}
339342
if debugNewTransaction {
@@ -379,6 +382,8 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
379382
ReadonlyIndexes []uint8
380383
Readonly []PublicKey
381384
})
385+
386+
message.AccountKeys = make([]PublicKey, 0, len(allKeys))
382387
for idx, acc := range allKeys {
383388

384389
if debugNewTransaction {
@@ -423,6 +428,14 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
423428
var lookupsWritableKeys []PublicKey
424429
var lookupsReadOnlyKeys []PublicKey
425430
if len(lookupsMap) > 0 {
431+
totalWritable, totalReadonly := 0, 0
432+
for _, l := range lookupsMap {
433+
totalWritable += len(l.Writable)
434+
totalReadonly += len(l.Readonly)
435+
}
436+
lookupsWritableKeys = make([]PublicKey, 0, totalWritable)
437+
lookupsReadOnlyKeys = make([]PublicKey, 0, totalReadonly)
438+
426439
lookups := make([]MessageAddressTableLookup, 0, len(lookupsMap))
427440

428441
sortedLookupKeys := make(PublicKeySlice, 0, len(lookupsMap))
@@ -453,17 +466,17 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
453466
}
454467

455468
var idx uint16
456-
accountKeyIndex := make(map[string]uint16, len(message.AccountKeys)+len(lookupsWritableKeys)+len(lookupsReadOnlyKeys))
469+
accountKeyIndex := make(map[PublicKey]uint16, len(message.AccountKeys)+len(lookupsWritableKeys)+len(lookupsReadOnlyKeys))
457470
for _, acc := range message.AccountKeys {
458-
accountKeyIndex[acc.String()] = idx
471+
accountKeyIndex[acc] = idx
459472
idx++
460473
}
461474
for _, acc := range lookupsWritableKeys {
462-
accountKeyIndex[acc.String()] = idx
475+
accountKeyIndex[acc] = idx
463476
idx++
464477
}
465478
for _, acc := range lookupsReadOnlyKeys {
466-
accountKeyIndex[acc.String()] = idx
479+
accountKeyIndex[acc] = idx
467480
idx++
468481
}
469482

@@ -479,14 +492,14 @@ func NewTransaction(instructions []Instruction, recentBlockHash Hash, opts ...Tr
479492
accounts = instruction.Accounts()
480493
accountIndex := make([]uint16, len(accounts))
481494
for idx, acc := range accounts {
482-
accountIndex[idx] = accountKeyIndex[acc.PublicKey.String()]
495+
accountIndex[idx] = accountKeyIndex[acc.PublicKey]
483496
}
484497
data, err := instruction.Data()
485498
if err != nil {
486499
return nil, fmt.Errorf("unable to encode instructions [%d]: %w", txIdx, err)
487500
}
488501
message.Instructions = append(message.Instructions, CompiledInstruction{
489-
ProgramIDIndex: accountKeyIndex[instruction.ProgramID().String()],
502+
ProgramIDIndex: accountKeyIndex[instruction.ProgramID()],
490503
Accounts: accountIndex,
491504
Data: data,
492505
})
@@ -576,15 +589,38 @@ func (tx *Transaction) PartialSign(getter privateKeyGetter) (out []Signature, er
576589
return nil, fmt.Errorf("invalid signatures length, expected %d, actual %d", len(signerKeys), len(tx.Signatures))
577590
}
578591

579-
for i, key := range signerKeys {
580-
privateKey := getter(key)
581-
if privateKey != nil {
582-
s, err := privateKey.Sign(messageContent)
583-
if err != nil {
584-
return nil, fmt.Errorf("failed to signed with key %q: %w", key.String(), err)
592+
// if signerKeys is len 1, spawning a goroutine is needless work
593+
if len(signerKeys) > 1 {
594+
var g errgroup.Group
595+
for i, key := range signerKeys {
596+
privateKey := getter(key)
597+
if privateKey == nil {
598+
continue
599+
}
600+
g.Go(func() error {
601+
s, err := privateKey.Sign(messageContent)
602+
if err != nil {
603+
return fmt.Errorf("failed to sign with key %q: %w", key.String(), err)
604+
}
605+
// Directly assign the signature to the corresponding position in the transaction's signature slice
606+
tx.Signatures[i] = s
607+
return nil
608+
})
609+
}
610+
if err := g.Wait(); err != nil {
611+
return nil, err
612+
}
613+
} else {
614+
for i, key := range signerKeys {
615+
privateKey := getter(key)
616+
if privateKey != nil {
617+
s, err := privateKey.Sign(messageContent)
618+
if err != nil {
619+
return nil, fmt.Errorf("failed to signed with key %q: %w", key.String(), err)
620+
}
621+
// Directly assign the signature to the corresponding position in the transaction's signature slice
622+
tx.Signatures[i] = s
585623
}
586-
// Directly assign the signature to the corresponding position in the transaction's signature slice
587-
tx.Signatures[i] = s
588624
}
589625
}
590626
return tx.Signatures, nil

0 commit comments

Comments
 (0)