-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstarkGRPC.go
188 lines (162 loc) · 5.09 KB
/
starkGRPC.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
package stark
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/pkg/errors"
starkhelpers "github.com/will-rowe/stark/src/helpers"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
// Get will retrieve a copy of a Record
// from the starkDB using the provided
// lookup key.
func (starkdb *Db) Get(ctx context.Context, key *Key) (*Response, error) {
starkdb.Lock()
defer starkdb.Unlock()
// check the local keystore for the provided key
cid, ok := starkdb.cidLookup[key.GetKey()]
if !ok {
return nil, status.Error(codes.NotFound, fmt.Sprintf("no Record in database for key: %v", key.GetKey()))
}
// use the helper method to retrieve the Record
record, err := starkdb.getRecordFromCID(cid)
if err != nil {
return nil, err
}
starkdb.send2log(fmt.Sprintf("record retrieved: %v->%v", key.GetKey(), cid))
return &Response{Success: true, Record: record}, nil
}
// Set will add a a KeyRecordPair to the
// starkDB.
//
// This method will add comments to the
// Record's history before adding it to the
// IPFS.
//
// It will return a reponse, which contains a
// copy of the Record that was added to the
// starkDB (which contains the updated Record
// history and the CID for the Record in the
// IPFS).
func (starkdb *Db) Set(ctx context.Context, krp *KeyRecordPair) (*Response, error) {
starkdb.Lock()
defer starkdb.Unlock()
// check the provided key and value
key := krp.GetKey()
record := krp.GetRecord()
// check the key
if len(key) == 0 {
return nil, ErrNoKey
}
// check the local keystore to see if this key has been used before
if existingCID, exists := starkdb.cidLookup[key]; exists {
// retrieve the record for this key
existingRecord, err := starkdb.getRecordFromCID(existingCID)
if err != nil {
return nil, err
}
// check UUIDs
if existingRecord.GetUuid() != record.GetUuid() {
return nil, ErrAttemptedOverwrite
}
// if the existingCID in the local keystore does not match the previousCID of the incoming Record it is an attempted overwrite
if existingCID != record.GetPreviousCID() {
return nil, ErrRecordHistory
}
// otherwise this is an attempted update, check that the incoming Record is more recent
if !starkhelpers.CheckTimeStamp(existingRecord.GetLastUpdatedTimestamp(), record.GetLastUpdatedTimestamp()) {
return nil, ErrAttemptedUpdate
}
record.AddComment("Set: updating record.")
}
record.AddComment("Set: adding record to IPFS.")
// if encrypting requested and Record isn't already, do it now
if len(starkdb.cipherKey) != 0 && !record.GetEncrypted() {
if err := record.Encrypt(starkdb.cipherKey); err != nil {
return nil, err
}
}
// marshal Record data to JSON
jsonData, err := json.Marshal(record)
if err != nil {
return nil, err
}
// create DAG node in IPFS for Record data
cid, err := starkdb.ipfsClient.DagPut(ctx, jsonData, starkdb.pinning)
if err != nil {
return nil, err
}
// link the record CID to the project directory and take a snapshot
snapshotUpdate, err := starkdb.ipfsClient.AddLink(ctx, starkdb.snapshotCID, cid, key)
if err != nil {
return nil, errors.Wrap(err, ErrSnapshotUpdate.Error())
}
starkdb.snapshotCID = snapshotUpdate
// if announcing, do it now
if starkdb.announcing {
// TODO: send proto data instead of CID
if err := starkdb.publishAnnouncement([]byte(cid)); err != nil {
return nil, err
}
}
// add the returned CID to the local keystore
starkdb.cidLookup[key] = cid
starkdb.currentNumEntries++
starkdb.sessionEntries++
// job done
starkdb.send2log(fmt.Sprintf("record added: %v->%v", key, cid))
// use pinata if session interval reached
if starkdb.pinataInterval > 0 {
if starkdb.sessionEntries%starkdb.pinataInterval == 0 {
starkdb.send2log("pinning interval reached, uploading database to Pinata")
var k, s string
var ok1, ok2 bool
if k, ok1 = os.LookupEnv(DefaultPinataAPIkey); !ok1 {
return nil, ErrPinataKey
}
if s, ok2 = os.LookupEnv(DefaultPinataSecretKey); !ok2 {
return nil, ErrPinataSecret
}
go func() {
pinataResp, err := starkdb.PinataPublish(k, s)
if err != nil {
starkdb.send2log(fmt.Sprintf("pinata error: %v", err))
return
}
starkdb.send2log(fmt.Sprintf("pinata API response: %v", pinataResp.Status))
}()
}
}
// add the CID to the record and return
record.PreviousCID = cid
return &Response{Success: true, Record: record}, nil
}
// Dump returns the metadata from a starkDB instance.
//
// Note: input key is currently unused.
func (starkdb *Db) Dump(ctx context.Context, empty *emptypb.Empty) (*DbMeta, error) {
starkdb.Lock()
defer starkdb.Unlock()
nodeAdd, err := starkdb.GetNodeAddr()
if err != nil {
return nil, err
}
peerAdds, err := starkdb.GetConnectedPeers()
if err != nil {
return nil, err
}
starkdb.send2log("database dumped")
return &DbMeta{
Project: starkdb.project,
Snapshot: starkdb.snapshotCID,
NodeAddress: nodeAdd,
Peers: peerAdds,
Pinning: starkdb.pinning,
Announcing: starkdb.announcing,
CurrEntries: int32(starkdb.currentNumEntries),
Pairs: starkdb.cidLookup,
}, nil
}