/
model.go
276 lines (236 loc) · 8.86 KB
/
model.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
package entityrelationship
import (
"context"
"encoding/json"
"reflect"
"github.com/centrifuge/centrifuge-protobufs/documenttypes"
"github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument"
"github.com/centrifuge/centrifuge-protobufs/gen/go/entity"
"github.com/centrifuge/go-centrifuge/documents"
"github.com/centrifuge/go-centrifuge/errors"
"github.com/centrifuge/go-centrifuge/identity"
"github.com/centrifuge/go-centrifuge/protobufs/gen/go/document"
entitypb2 "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity"
"github.com/centrifuge/precise-proofs/proofs"
"github.com/centrifuge/precise-proofs/proofs/proto"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
)
const prefix string = "entity_relationship"
// tree prefixes for specific documents use the second byte of a 4 byte slice by convention
func compactPrefix() []byte { return []byte{0, 4, 0, 0} }
// EntityRelationship implements the documents.Model and keeps track of entity-relationship related fields and state.
type EntityRelationship struct {
*documents.CoreDocument
// owner of the relationship
OwnerIdentity *identity.DID
// Entity identifier
EntityIdentifier []byte
// identity which will be granted access
TargetIdentity *identity.DID
}
// getRelationshipData returns the entity relationship data from the entity relationship model
func (e *EntityRelationship) getRelationshipData() *entitypb2.RelationshipData {
dids := identity.DIDsToStrings(e.OwnerIdentity, e.TargetIdentity)
eID := hexutil.Encode(e.EntityIdentifier)
return &entitypb2.RelationshipData{
OwnerIdentity: dids[0],
TargetIdentity: dids[1],
EntityIdentifier: eID,
}
}
// createP2PProtobuf returns Centrifuge protobuf-specific RelationshipData.
func (e *EntityRelationship) createP2PProtobuf() *entitypb.EntityRelationship {
dids := identity.DIDsToBytes(e.OwnerIdentity, e.TargetIdentity)
return &entitypb.EntityRelationship{
OwnerIdentity: dids[0],
TargetIdentity: dids[1],
EntityIdentifier: e.EntityIdentifier,
}
}
// InitEntityRelationshipInput initialize the model based on the received parameters from the rest api call
func (e *EntityRelationship) InitEntityRelationshipInput(ctx context.Context, entityID string, data *entitypb2.RelationshipData) error {
if err := e.initEntityRelationshipFromData(data); err != nil {
return err
}
params := documentpb.AccessTokenParams{
Grantee: data.TargetIdentity,
DocumentIdentifier: entityID,
}
cd, err := documents.NewCoreDocumentWithAccessToken(ctx, compactPrefix(), params)
if err != nil {
return errors.New("failed to init core document: %v", err)
}
e.CoreDocument = cd
return nil
}
// PrepareNewVersion prepares new version from the old entity.
func (e *EntityRelationship) PrepareNewVersion(old documents.Model, data *entitypb2.RelationshipData, collaborators []string) error {
err := e.initEntityRelationshipFromData(data)
if err != nil {
return err
}
oldCD := old.(*EntityRelationship).CoreDocument
e.CoreDocument, err = oldCD.PrepareNewVersion(compactPrefix(), documents.CollaboratorsAccess{})
if err != nil {
return err
}
return nil
}
// initEntityRelationshipFromData initialises an EntityRelationship from RelationshipData.
func (e *EntityRelationship) initEntityRelationshipFromData(data *entitypb2.RelationshipData) error {
dids, err := identity.StringsToDIDs(data.OwnerIdentity, data.TargetIdentity)
if err != nil {
return err
}
eID, err := hexutil.Decode(data.EntityIdentifier)
if err != nil {
return err
}
e.OwnerIdentity = dids[0]
e.TargetIdentity = dids[1]
e.EntityIdentifier = eID
return nil
}
// loadFromP2PProtobuf loads the Entity Relationship from Centrifuge protobuf.
func (e *EntityRelationship) loadFromP2PProtobuf(entityRelationship *entitypb.EntityRelationship) error {
dids, err := identity.BytesToDIDs(entityRelationship.OwnerIdentity, entityRelationship.TargetIdentity)
if err != nil {
return err
}
e.OwnerIdentity = dids[0]
e.TargetIdentity = dids[1]
e.EntityIdentifier = entityRelationship.EntityIdentifier
return nil
}
// PackCoreDocument packs the EntityRelationship into a CoreDocument.
func (e *EntityRelationship) PackCoreDocument() (cd coredocumentpb.CoreDocument, err error) {
entityRelationship := e.createP2PProtobuf()
data, err := proto.Marshal(entityRelationship)
if err != nil {
return cd, errors.New("couldn't serialise EntityData: %v", err)
}
embedData := &any.Any{
TypeUrl: e.DocumentType(),
Value: data,
}
return e.CoreDocument.PackCoreDocument(embedData), nil
}
// UnpackCoreDocument unpacks the core document into an EntityRelationship.
func (e *EntityRelationship) UnpackCoreDocument(cd coredocumentpb.CoreDocument) error {
if cd.EmbeddedData == nil ||
cd.EmbeddedData.TypeUrl != e.DocumentType() {
return errors.New("trying to convert document with incorrect schema")
}
entityRelationship := new(entitypb.EntityRelationship)
err := proto.Unmarshal(cd.EmbeddedData.Value, entityRelationship)
if err != nil {
return err
}
err = e.loadFromP2PProtobuf(entityRelationship)
if err != nil {
return err
}
e.CoreDocument = documents.NewCoreDocumentFromProtobuf(cd)
return nil
}
// JSON marshals EntityRelationship into a json bytes
func (e *EntityRelationship) JSON() ([]byte, error) {
return json.Marshal(e)
}
// FromJSON unmarshals the json bytes into EntityRelationship
func (e *EntityRelationship) FromJSON(jsonData []byte) error {
return json.Unmarshal(jsonData, e)
}
// Type gives the EntityRelationship type.
func (e *EntityRelationship) Type() reflect.Type {
return reflect.TypeOf(e)
}
// CalculateDataRoot calculates the data root.
func (e *EntityRelationship) CalculateDataRoot() ([]byte, error) {
t, err := e.getDocumentDataTree()
if err != nil {
return nil, errors.New("failed to get data tree: %v", err)
}
dr := t.RootHash()
return dr, nil
}
// getDocumentDataTree creates precise-proofs data tree for the model
func (e *EntityRelationship) getDocumentDataTree() (tree *proofs.DocumentTree, err error) {
eProto := e.createP2PProtobuf()
if err != nil {
return nil, err
}
if e.CoreDocument == nil {
return nil, errors.New("getDocumentDataTree error CoreDocument not set")
}
t := e.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix())
if err := t.AddLeavesFromDocument(eProto); err != nil {
return nil, errors.New("getDocumentDataTree error %v", err)
}
if err := t.Generate(); err != nil {
return nil, errors.New("getDocumentDataTree error %v", err)
}
return t, nil
}
// CreateNFTProofs is not implemented for EntityRelationship.
func (e *EntityRelationship) CreateNFTProofs(
account identity.DID,
registry common.Address,
tokenID []byte,
nftUniqueProof, readAccessProof bool) (proofs []*proofspb.Proof, err error) {
return nil, documents.ErrNotImplemented
}
// CreateProofs generates proofs for given fields.
func (e *EntityRelationship) CreateProofs(fields []string) (proofs []*proofspb.Proof, err error) {
tree, err := e.getDocumentDataTree()
if err != nil {
return nil, errors.New("createProofs error %v", err)
}
return e.CoreDocument.CreateProofs(e.DocumentType(), tree, fields)
}
// DocumentType returns the entity relationship document type.
func (*EntityRelationship) DocumentType() string {
return documenttypes.EntityRelationshipDataTypeUrl
}
// AddNFT is not implemented for EntityRelationship
func (e *EntityRelationship) AddNFT(grantReadAccess bool, registry common.Address, tokenID []byte) error {
return documents.ErrNotImplemented
}
// CalculateSigningRoot calculates the signing root of the document.
func (e *EntityRelationship) CalculateSigningRoot() ([]byte, error) {
dr, err := e.CalculateDataRoot()
if err != nil {
return dr, err
}
return e.CoreDocument.CalculateSigningRoot(e.DocumentType(), dr)
}
// CalculateDocumentRoot calculates the document root.
func (e *EntityRelationship) CalculateDocumentRoot() ([]byte, error) {
dr, err := e.CalculateDataRoot()
if err != nil {
return dr, err
}
return e.CoreDocument.CalculateDocumentRoot(e.DocumentType(), dr)
}
// DocumentRootTree creates and returns the document root tree.
func (e *EntityRelationship) DocumentRootTree() (tree *proofs.DocumentTree, err error) {
dr, err := e.CalculateDataRoot()
if err != nil {
return nil, err
}
return e.CoreDocument.DocumentRootTree(e.DocumentType(), dr)
}
// CollaboratorCanUpdate checks that the identity attempting to update the document is the identity which owns the document.
func (e *EntityRelationship) CollaboratorCanUpdate(updated documents.Model, identity identity.DID) error {
newEntityRelationship, ok := updated.(*EntityRelationship)
if !ok {
return errors.NewTypedError(documents.ErrDocumentInvalidType, errors.New("expecting an entity relationship but got %T", updated))
}
if !e.OwnerIdentity.Equal(identity) || !newEntityRelationship.OwnerIdentity.Equal(identity) {
return documents.ErrIdentityNotOwner
}
return nil
}