/
util.go
194 lines (172 loc) · 6.02 KB
/
util.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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package util
import (
"context"
"crypto/rand"
"strings"
"time"
"unicode"
"github.com/brianvoe/gofakeit/v6"
"github.com/cockroachdb/errors"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/ybbus/jsonrpc/v3"
)
// NextPowerOfTwo calculates the smallest power of two that is greater than or equal to x.
// If x is already a power of two, it returns x. For x equal to 0, the result is 1.
//
// Parameters:
// - x: The input value for which the next power of two needs to be calculated.
//
// Returns:
// The smallest power of two that is greater than or equal to x.
func NextPowerOfTwo(x uint64) uint64 {
if x == 0 {
return 1
}
// Find the position of the highest bit set to 1
pos := uint(0)
for shifted := x; shifted > 0; shifted >>= 1 {
pos++
}
// If x is already a power of two, return x
if x == 1<<(pos-1) {
return x
}
// Otherwise, return the next power of two
return 1 << pos
}
// NewLotusClient is a function that creates a new JSON-RPC client for interacting with a Lotus node.
// It takes the Lotus API endpoint and an optional Lotus token as input.
// If the Lotus token is provided, it is included in the 'Authorization' header of the JSON-RPC requests.
//
// Parameters:
//
// - lotusAPI: The Lotus API endpoint. Must be a valid URL.
// - lotusToken: An optional Lotus token. If provided, it is included in the 'Authorization' header of the JSON-RPC requests.
//
// Returns:
//
// A JSON-RPC client configured to interact with the specified Lotus node. If a Lotus token is provided, the client includes it in the 'Authorization' header of its requests.
func NewLotusClient(lotusAPI string, lotusToken string) jsonrpc.RPCClient {
if lotusToken == "" {
return jsonrpc.NewClient(lotusAPI)
} else {
return jsonrpc.NewClientWithOpts(lotusAPI, &jsonrpc.RPCClientOpts{
CustomHeaders: map[string]string{
"Authorization": "Bearer " + lotusToken,
},
})
}
}
// IsAllDigits is a function that checks if a string contains only digits.
func IsAllDigits(s string) bool {
for _, r := range s {
if !unicode.IsDigit(r) {
return false
}
}
return true
}
// ChunkMapKeys is a generic function that takes a map with keys of any comparable type and values of any type, and an integer as input.
// It divides the keys of the input map into packJobs of size 'chunkSize' and returns a 2D slice of keys.
// It uses the ChunkSlice function to divide the keys into packJobs.
//
// Parameters:
//
// - m: A map with keys of any comparable type and values of any type. The keys of this map will be chunked.
// - chunkSize: The size of each packJob. Must be a positive integer.
//
// Returns:
//
// - A 2D slice where each inner slice is of length 'chunkSize'. The last inner slice may be shorter if the number of keys in 'm' is not a multiple of 'chunkSize'.
func ChunkMapKeys[T1 comparable, T2 any](m map[T1]T2, chunkSize int) [][]T1 {
keys := make([]T1, 0, len(m))
for key := range m {
keys = append(keys, key)
}
return ChunkSlice(keys, chunkSize)
}
// ChunkSlice is a generic function that takes a slice of any type and an integer as input.
// It divides the input slice into packJobs of size 'chunkSize' and returns a 2D slice.
// If 'chunkSize' is less than or equal to zero, it returns an empty 2D slice.
//
// Parameters:
//
// - slice: A slice of any type that needs to be chunked.
// - chunkSize: The size of each packJob. Must be a positive integer.
//
// Returns:
//
// - A 2D slice where each inner slice is of length 'chunkSize'.
// The last inner slice may be shorter if the length of 'slice' is not a multiple of 'chunkSize'.
func ChunkSlice[T any](slice []T, chunkSize int) [][]T {
var packJobs [][]T
if chunkSize <= 0 {
return packJobs
}
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}
packJobs = append(packJobs, slice[i:end])
}
return packJobs
}
const BatchSize = 100
// GenerateNewPeer is a function that generates a new peer for the IPFS network.
// It generates a new Ed25519 key pair using a secure random source and derives a peer ID from the public key.
// It then marshals the private and public keys into byte slices.
//
// Returns:
//
// The private key as a byte slice, the public key as a byte slice, the peer ID, and an error if the operation failed.
// If any of the operations (key generation, peer ID derivation, key marshalling) fail, it returns an error wrapped with a descriptive message.
func GenerateNewPeer() ([]byte, []byte, peer.ID, error) {
private, public, err := crypto.GenerateEd25519Key(rand.Reader)
if err != nil {
return nil, nil, "", errors.Wrap(err, "cannot generate new peer")
}
peerID, err := peer.IDFromPublicKey(public)
if err != nil {
return nil, nil, "", errors.Wrap(err, "cannot generate peer id")
}
privateBytes, err := crypto.MarshalPrivateKey(private)
if err != nil {
return nil, nil, "", errors.Wrap(err, "cannot marshal private key")
}
publicBytes, err := crypto.MarshalPublicKey(public)
if err != nil {
return nil, nil, "", errors.Wrap(err, "cannot marshal public key")
}
return privateBytes, publicBytes, peerID, nil
}
var ErrNotImplemented = errors.New("not implemented")
func RandomName() string {
for {
candidate := gofakeit.AdjectiveDescriptive() + "_" + gofakeit.NounCollectiveThing()
if strings.ContainsRune(candidate, ' ') {
continue
}
return strings.ToLower(candidate)
}
}
// GetLotusHeadTime retrieves the timestamp of the latest block in the Lotus API and returns it as a time.Time value.
//
//nolint:tagliatelle
func GetLotusHeadTime(ctx context.Context, lotusAPI string, lotusToken string) (time.Time, error) {
client := NewLotusClient(lotusAPI, lotusToken)
var resp struct {
Blocks []struct {
Timestamp int64 `json:"Timestamp"`
} `json:"Blocks"`
}
err := client.CallFor(ctx, &resp, "Filecoin.ChainHead")
if err != nil {
return time.Time{}, errors.Wrap(err, "failed to get chain head")
}
if len(resp.Blocks) == 0 {
return time.Time{}, errors.New("chain head is empty")
}
return time.Unix(resp.Blocks[0].Timestamp, 0), nil
}