/
rotation_signer.go
69 lines (60 loc) · 2.29 KB
/
rotation_signer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package requester
import (
"fmt"
"github.com/onflow/flow-go-sdk/crypto"
"sync"
)
var _ crypto.Signer = &KeyRotationSigner{}
// KeyRotationSigner is a crypto signer that contains a pool of key pairs to sign with,
// and it rotates the key used for each signing request. This allows for faster
// submission of transactions to the network, due to a sequence number not being reused
// between different keys used.
// It also contains logic to queue up signature requests and in case there are more
// transactions pending than the keys in the pool it will wait for transactions to
// get executed so the new sequence key can be obtained.
// The signer is concurrency-safe.
type KeyRotationSigner struct {
mux sync.RWMutex
keys []crypto.PrivateKey
hasher crypto.Hasher
index int
keyLen int
}
func NewKeyRotationSigner(keys []crypto.PrivateKey, hashAlgo crypto.HashAlgorithm) (*KeyRotationSigner, error) {
// check compatibility to form a signing key
for _, pk := range keys {
if !crypto.CompatibleAlgorithms(pk.Algorithm(), hashAlgo) {
return nil, fmt.Errorf("signature algorithm %s and hashing algorithm are incompatible %s",
pk.Algorithm(), hashAlgo)
}
}
hasher, err := crypto.NewHasher(hashAlgo)
if err != nil {
return nil, fmt.Errorf("signer with hasher %s can't be instantiated with this function", hashAlgo)
}
return &KeyRotationSigner{
keys: keys,
hasher: hasher,
keyLen: len(keys),
}, nil
}
// Sign signs the message and then rotates to the next key.
// note: if you want to get the public key pair, you should first call
// PublicKey and then Sign.
func (k *KeyRotationSigner) Sign(message []byte) ([]byte, error) {
k.mux.Lock()
defer k.mux.Unlock()
sig, err := k.keys[k.index].Sign(message, k.hasher)
k.index = (k.index + 1) % k.keyLen
return sig, err
}
// PublicKey returns the current public key which is available for signing.
func (k *KeyRotationSigner) PublicKey() crypto.PublicKey {
// todo make this function to wait if the transactions that used this public key
// is not yet executed on the network, this is so it prevents using
// the same public key for fetching key sequence number before the transaction
// that already used it is not executed and thus the key would be incremented.
k.mux.RLock()
defer k.mux.RUnlock()
return k.keys[k.index].PublicKey()
}