mirror of
https://github.com/anyproto/any-sync.git
synced 2025-06-08 05:57:03 +09:00
257 lines
6.3 KiB
Go
257 lines
6.3 KiB
Go
package objecttree
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/anytypeio/any-sync/commonspace/object/keychain"
|
|
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
|
|
"github.com/anytypeio/any-sync/util/cidutil"
|
|
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
|
|
"github.com/anytypeio/any-sync/util/keys/symmetric"
|
|
"github.com/gogo/protobuf/proto"
|
|
"time"
|
|
)
|
|
|
|
var ErrEmptyChange = errors.New("change payload should not be empty")
|
|
|
|
type BuilderContent struct {
|
|
TreeHeadIds []string
|
|
AclHeadId string
|
|
SnapshotBaseId string
|
|
CurrentReadKeyHash uint64
|
|
Identity []byte
|
|
IsSnapshot bool
|
|
SigningKey signingkey.PrivKey
|
|
ReadKey *symmetric.Key
|
|
Content []byte
|
|
}
|
|
|
|
type InitialContent struct {
|
|
AclHeadId string
|
|
Identity []byte
|
|
SigningKey signingkey.PrivKey
|
|
SpaceId string
|
|
Seed []byte
|
|
ChangeType string
|
|
Timestamp int64
|
|
}
|
|
|
|
type ChangeBuilder interface {
|
|
ConvertFromRaw(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error)
|
|
BuildContent(payload BuilderContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error)
|
|
BuildInitialContent(payload InitialContent) (ch *Change, raw *treechangeproto.RawTreeChangeWithId, err error)
|
|
BuildRaw(ch *Change) (*treechangeproto.RawTreeChangeWithId, error)
|
|
SetRootRawChange(rawIdChange *treechangeproto.RawTreeChangeWithId)
|
|
}
|
|
|
|
type changeBuilder struct {
|
|
rootChange *treechangeproto.RawTreeChangeWithId
|
|
keys *keychain.Keychain
|
|
}
|
|
|
|
func NewChangeBuilder(keys *keychain.Keychain, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
|
|
return &changeBuilder{keys: keys, rootChange: rootChange}
|
|
}
|
|
|
|
func (c *changeBuilder) ConvertFromRaw(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error) {
|
|
if rawIdChange.GetRawChange() == nil {
|
|
err = ErrEmptyChange
|
|
return
|
|
}
|
|
|
|
if verify {
|
|
// verifying ID
|
|
if !cidutil.VerifyCid(rawIdChange.RawChange, rawIdChange.Id) {
|
|
err = ErrIncorrectCid
|
|
return
|
|
}
|
|
}
|
|
|
|
raw := &treechangeproto.RawTreeChange{} // TODO: sync pool
|
|
err = proto.Unmarshal(rawIdChange.GetRawChange(), raw)
|
|
if err != nil {
|
|
return
|
|
}
|
|
ch, err = c.unmarshallRawChange(raw, rawIdChange.Id)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if verify {
|
|
var identityKey signingkey.PubKey
|
|
identityKey, err = c.keys.GetOrAdd(ch.Identity)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// verifying signature
|
|
var res bool
|
|
res, err = identityKey.Verify(raw.Payload, raw.Signature)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !res {
|
|
err = ErrIncorrectSignature
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *changeBuilder) SetRootRawChange(rawIdChange *treechangeproto.RawTreeChangeWithId) {
|
|
c.rootChange = rawIdChange
|
|
}
|
|
|
|
func (c *changeBuilder) BuildInitialContent(payload InitialContent) (ch *Change, rawIdChange *treechangeproto.RawTreeChangeWithId, err error) {
|
|
change := &treechangeproto.RootChange{
|
|
AclHeadId: payload.AclHeadId,
|
|
Timestamp: payload.Timestamp,
|
|
Identity: payload.Identity,
|
|
ChangeType: payload.ChangeType,
|
|
SpaceId: payload.SpaceId,
|
|
Seed: payload.Seed,
|
|
}
|
|
|
|
marshalledChange, err := proto.Marshal(change)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
signature, err := payload.SigningKey.Sign(marshalledChange)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
raw := &treechangeproto.RawTreeChange{
|
|
Payload: marshalledChange,
|
|
Signature: signature,
|
|
}
|
|
|
|
marshalledRawChange, err := proto.Marshal(raw)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
id, err := cidutil.NewCidFromBytes(marshalledRawChange)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ch = NewChangeFromRoot(id, change, signature)
|
|
|
|
rawIdChange = &treechangeproto.RawTreeChangeWithId{
|
|
RawChange: marshalledRawChange,
|
|
Id: id,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *changeBuilder) BuildContent(payload BuilderContent) (ch *Change, rawIdChange *treechangeproto.RawTreeChangeWithId, err error) {
|
|
change := &treechangeproto.TreeChange{
|
|
TreeHeadIds: payload.TreeHeadIds,
|
|
AclHeadId: payload.AclHeadId,
|
|
SnapshotBaseId: payload.SnapshotBaseId,
|
|
CurrentReadKeyHash: payload.CurrentReadKeyHash,
|
|
Timestamp: time.Now().UnixNano(),
|
|
Identity: payload.Identity,
|
|
IsSnapshot: payload.IsSnapshot,
|
|
}
|
|
if payload.ReadKey != nil {
|
|
var encrypted []byte
|
|
encrypted, err = payload.ReadKey.Encrypt(payload.Content)
|
|
if err != nil {
|
|
return
|
|
}
|
|
change.ChangesData = encrypted
|
|
} else {
|
|
change.ChangesData = payload.Content
|
|
}
|
|
|
|
marshalledChange, err := proto.Marshal(change)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
signature, err := payload.SigningKey.Sign(marshalledChange)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
raw := &treechangeproto.RawTreeChange{
|
|
Payload: marshalledChange,
|
|
Signature: signature,
|
|
}
|
|
|
|
marshalledRawChange, err := proto.Marshal(raw)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
id, err := cidutil.NewCidFromBytes(marshalledRawChange)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ch = NewChange(id, change, signature)
|
|
|
|
rawIdChange = &treechangeproto.RawTreeChangeWithId{
|
|
RawChange: marshalledRawChange,
|
|
Id: id,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *changeBuilder) BuildRaw(ch *Change) (raw *treechangeproto.RawTreeChangeWithId, err error) {
|
|
if ch.Id == c.rootChange.Id {
|
|
return c.rootChange, nil
|
|
}
|
|
treeChange := &treechangeproto.TreeChange{
|
|
TreeHeadIds: ch.PreviousIds,
|
|
AclHeadId: ch.AclHeadId,
|
|
SnapshotBaseId: ch.SnapshotId,
|
|
ChangesData: ch.Data,
|
|
CurrentReadKeyHash: ch.ReadKeyHash,
|
|
Timestamp: ch.Timestamp,
|
|
Identity: []byte(ch.Identity),
|
|
IsSnapshot: ch.IsSnapshot,
|
|
}
|
|
var marshalled []byte
|
|
marshalled, err = treeChange.Marshal()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
marshalledRawChange, err := proto.Marshal(&treechangeproto.RawTreeChange{
|
|
Payload: marshalled,
|
|
Signature: ch.Signature,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
raw = &treechangeproto.RawTreeChangeWithId{
|
|
RawChange: marshalledRawChange,
|
|
Id: ch.Id,
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *changeBuilder) unmarshallRawChange(raw *treechangeproto.RawTreeChange, id string) (ch *Change, err error) {
|
|
if c.rootChange.Id == id {
|
|
unmarshalled := &treechangeproto.RootChange{}
|
|
err = proto.Unmarshal(raw.Payload, unmarshalled)
|
|
if err != nil {
|
|
return
|
|
}
|
|
ch = NewChangeFromRoot(id, unmarshalled, raw.Signature)
|
|
return
|
|
}
|
|
|
|
unmarshalled := &treechangeproto.TreeChange{}
|
|
err = proto.Unmarshal(raw.Payload, unmarshalled)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ch = NewChange(id, unmarshalled, raw.Signature)
|
|
return
|
|
}
|