/
universe.go
323 lines (299 loc) · 8.69 KB
/
universe.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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// Copyright 2019 The PDU Authors
// This file is part of the PDU library.
//
// The PDU library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The PDU library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the PDU library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"encoding/json"
"github.com/pdupub/go-pdu/common"
"github.com/pdupub/go-pdu/dag"
)
// Universe is the struct contain all the information can be received and validated.
// It is built with two precreate users as root users. Any message should be validated before
// add into msgD, which is a DAG used to store message. If new user create from valid message,
// the user should add into userD, a DAG used to store all users. The universe contain at least
// one spacetime, each spacetime base on one user msg as time line. validation of message mean
// this message should be valid at least in one of spacetime in stD. Information in local universe
// is only part of information in whole decentralized system.
type Universe struct {
msgD *dag.DAG // contain all messages valid in at least one spacetime
userD *dag.DAG // contain all users valid in at least one spacetime (strict)
stD *dag.DAG // contain all spacetime, which could be diff by selecting (strict)
}
// NewUniverse create Universe with two user with diff gender as root users
func NewUniverse(Eve, Adam *User) (*Universe, error) {
if Eve.Gender() == Adam.Gender() {
return nil, ErrNotSupportYet
}
EveVertex, err := dag.NewVertex(Eve.ID(), Eve)
if err != nil {
return nil, err
}
AdamVertex, err := dag.NewVertex(Adam.ID(), Adam)
if err != nil {
return nil, err
}
userD, err := dag.NewDAG(2, EveVertex, AdamVertex)
if err != nil {
return nil, err
}
userD.SetMaxParentsCount(2)
return &Universe{userD: userD}, nil
}
// AddMsg will check if the message from valid user, who is validated in at least one spacetime
// (in stD). Then new message will be added into Universe and update time proof if msg.SenderID
// is any spacetime based on.
func (u *Universe) AddMsg(msg *Message) error {
if !u.CheckUserExist(msg.SenderID) {
return ErrUserNotExist
}
if u.msgD == nil {
if err := u.initializeMsgD(msg); err != nil {
return err
}
if err := u.AddSpaceTime(msg, nil); err != nil {
return err
}
} else {
// check
if u.GetMsgByID(msg.ID()) != nil {
return ErrMsgAlreadyExist
}
// update dag
var refs []interface{}
for _, r := range msg.Reference {
refs = append(refs, r.MsgID)
}
msgVertex, err := dag.NewVertex(msg.ID(), msg, refs...)
if err != nil {
return err
}
err = u.msgD.AddVertex(msgVertex)
if err != nil {
return err
}
// update tp
err = u.updateTimeProof(msg)
if err != nil {
return err
}
// process the msg
err = u.processMsg(msg)
if err != nil {
return err
}
}
return nil
}
// GetSpaceTimeIDs get ids in of spacetime (list of msg.SenderID of each spacetime)
func (u *Universe) GetSpaceTimeIDs() []common.Hash {
var ids []common.Hash
if u.stD != nil {
for _, id := range u.stD.GetIDs() {
ids = append(ids, id.(common.Hash))
}
}
return ids
}
// AddSpaceTime will add spacetime in Universe with msg.SenderID, and follow the
// time sequence from ref.
func (u *Universe) AddSpaceTime(msg *Message, ref *MsgReference) error {
if u.GetMsgByID(msg.ID()) == nil {
return ErrMsgNotFound
}
if u.stD != nil && nil != u.stD.GetVertex(msg.SenderID) {
return ErrTPAlreadyExist
}
if !u.CheckUserExist(msg.SenderID) {
return ErrUserNotExist
}
// update time proof
initialize := true
startRecord := false
for _, id := range u.msgD.GetIDs() {
if id == msg.ID() {
startRecord = true
}
if msgSpaceTime := u.GetMsgByID(id); msgSpaceTime != nil && msgSpaceTime.SenderID == msg.SenderID && startRecord {
if initialize {
if err := u.initializeSpaceTime(msgSpaceTime, ref); err != nil {
return err
}
initialize = false
} else {
if err := u.updateTimeProof(msgSpaceTime); err != nil {
return err
}
}
}
}
return nil
}
func (u *Universe) initializeSpaceTime(msgSpaceTime *Message, ref *MsgReference) error {
st, err := NewSpaceTime(u, msgSpaceTime, ref)
if err != nil {
return err
}
var stVertex *dag.Vertex
if ref != nil {
stVertex, err = dag.NewVertex(msgSpaceTime.SenderID, st, ref.SenderID)
} else {
stVertex, err = dag.NewVertex(msgSpaceTime.SenderID, st)
}
if err != nil {
return err
}
if u.stD == nil {
stD, err := dag.NewDAG(1, stVertex)
if err != nil {
return err
}
u.stD = stD
} else {
if err = u.stD.AddVertex(stVertex); err != nil {
return err
}
}
return nil
}
// CheckUserExist check if the user valid in the Universe
func (u Universe) CheckUserExist(userID common.Hash) bool {
if nil != u.GetUserByID(userID) {
return true
}
return false
}
// GetUserByID return the user from userD, not userInfo by space time
func (u Universe) GetUserByID(userID common.Hash) *User {
if v := u.userD.GetVertex(userID); v != nil {
return v.Value().(*User)
}
return nil
}
// GetMaxSeq return the max time proof sequence
// time proof by the stID
func (u Universe) GetMaxSeq(stID common.Hash) uint64 {
if vertex := u.stD.GetVertex(stID); vertex != nil {
return vertex.Value().(*SpaceTime).maxTimeSequence
}
return 0
}
// GetUserIDs return userIDs in this space time
func (u Universe) GetUserIDs(stID common.Hash) []common.Hash {
var userIDs []common.Hash
if u.stD != nil {
if vertex := u.stD.GetVertex(stID); vertex != nil {
userIDs = vertex.Value().(*SpaceTime).GetUserIDs()
}
}
return userIDs
}
// GetUserInfo return the user info in space time
// return nil if not find user
func (u Universe) GetUserInfo(userID common.Hash, stID common.Hash) *UserInfo {
if u.stD != nil {
if stVertex := u.stD.GetVertex(stID); stVertex != nil {
return stVertex.Value().(*SpaceTime).GetUserInfo(userID)
}
}
return nil
}
// GetMsgByID will return the msg by msg.ID()
// nil will be return if msg not exist
func (u Universe) GetMsgByID(msgID interface{}) *Message {
if v := u.msgD.GetVertex(msgID); v != nil {
return v.Value().(*Message)
}
return nil
}
// initializeMsgD only run once to create u.msgD by initial message, and the DAG
// will remove strict rule, so msgD can accept new message if at least one of
// reference exist in whole universe.
func (u *Universe) initializeMsgD(msg *Message) error {
// build msg dag
msgVertex, err := dag.NewVertex(msg.ID(), msg)
if err != nil {
return err
}
msgD, err := dag.NewDAG(1, msgVertex)
if err != nil {
return err
}
msgD.RemoveStrict()
u.msgD = msgD
return nil
}
func (u *Universe) processMsg(msg *Message) error {
switch msg.Value.ContentType {
case TypeText:
return nil
case TypeDOB:
err := u.addUserByMsg(msg)
if err != nil {
return err
}
}
return nil
}
func (u *Universe) updateTimeProof(msg *Message) error {
if vertex := u.stD.GetVertex(msg.SenderID); vertex != nil {
return vertex.Value().(*SpaceTime).UpdateTimeProof(msg)
}
return nil
}
// addUser user to u.userD
// update info of u.stD need other func
func (u *Universe) addUserByMsg(msg *Message) error {
user, err := CreateNewUser(u, msg)
if err != nil {
return err
}
if u.GetUserByID(user.ID()) != nil {
return ErrUserAlreadyExist
}
var dobContent DOBMsgContent
err = json.Unmarshal(user.DOBMsg.Value.Content, &dobContent)
if err != nil {
return err
}
userAdded := false
for _, ref := range msg.Reference {
if err := u.addUserToSpaceTime(ref, dobContent, user); err != nil {
continue
}
// at least add into one space time
userAdded = true
}
if !userAdded {
return ErrNewUserAddFail
}
userVertex, err := dag.NewVertex(user.ID(), user, dobContent.Parents[0].UserID, dobContent.Parents[1].UserID)
if err != nil {
return err
}
err = u.userD.AddVertex(userVertex)
if err != nil {
return err
}
return nil
}
// addUserToSpaceTime used to add new user to spacetime base on ref.SenderID, the age of parents in this spacetime
// should fit the nature rule.
// TODO: ref.SenderID not must be spacetime, the new user's life length can be calculated by any ref msg.
func (u *Universe) addUserToSpaceTime(ref *MsgReference, dobContent DOBMsgContent, user *User) error {
if vertex := u.stD.GetVertex(ref.SenderID); vertex != nil {
return vertex.Value().(*SpaceTime).AddUser(ref, dobContent, user)
}
return ErrAddUserToSpaceTimeFail
}