-
Notifications
You must be signed in to change notification settings - Fork 6
/
operation.go
170 lines (137 loc) · 4.75 KB
/
operation.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
/*
Copyright Gen Digital Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package operationparser
import (
"encoding/json"
"errors"
"fmt"
"github.com/trustbloc/sidetree-go/pkg/api/operation"
"github.com/trustbloc/sidetree-go/pkg/api/protocol"
"github.com/trustbloc/sidetree-go/pkg/docutil"
logfields "github.com/trustbloc/sidetree-go/pkg/internal/log"
"github.com/trustbloc/sidetree-go/pkg/log"
"github.com/trustbloc/sidetree-go/pkg/versions/1_0/model"
)
var logger = log.New()
// Parser is an operation parser.
type Parser struct {
protocol.Protocol
anchorOriginValidator ObjectValidator
anchorTimeValidator TimeValidator
}
// New returns a new operation parser.
//
//nolint:gocritic
func New(p protocol.Protocol, opts ...Option) *Parser {
parser := &Parser{
Protocol: p,
}
// default anchor origin validator
parser.anchorOriginValidator = &objectValidator{}
// default anchor time validator
parser.anchorTimeValidator = &timeValidator{}
// apply options
for _, opt := range opts {
opt(parser)
}
return parser
}
// ObjectValidator validates object. Currently used for anchor origin validation
// however it can be used for any object validation.
type ObjectValidator interface {
Validate(obj interface{}) error
}
// Option is a parser instance option.
type Option func(opts *Parser)
// WithAnchorOriginValidator sets optional anchor origin validator.
func WithAnchorOriginValidator(v ObjectValidator) Option {
return func(opts *Parser) {
if v != nil {
opts.anchorOriginValidator = v
}
}
}
// ErrOperationExpired is thrown if anchor until time is less then reference time(e.g. server time or anchoring time).
var ErrOperationExpired = errors.New("operation expired")
// ErrOperationEarly is thrown if anchor from time is greater then reference time(e.g. server time or anchoring time).
var ErrOperationEarly = errors.New("operation early")
// TimeValidator validates earliest and expiry time for an operation against server time.
type TimeValidator interface {
Validate(from, until int64) error
}
// WithAnchorTimeValidator sets optional anchor time validator.
func WithAnchorTimeValidator(v TimeValidator) Option {
return func(opts *Parser) {
if v != nil {
opts.anchorTimeValidator = v
}
}
}
// Parse parses and validates operation.
func (p *Parser) Parse(namespace string, operationBuffer []byte) (*operation.Operation, error) {
// parse and validate operation buffer using this versions model and validation rules
internal, err := p.ParseOperation(namespace, operationBuffer, false)
if err != nil {
return nil, err
}
return &operation.Operation{
Type: internal.Type,
UniqueSuffix: internal.UniqueSuffix,
ID: internal.ID,
OperationRequest: operationBuffer,
}, nil
}
// ParseOperation parses and validates operation. Batch mode flag gives hints for the validation of
// operation object (anticipating future pruning/checkpoint requirements).
func (p *Parser) ParseOperation(namespace string, operationBuffer []byte, batch bool) (*model.Operation, error) {
// check maximum operation size against protocol before parsing
if len(operationBuffer) > int(p.MaxOperationSize) {
return nil, fmt.Errorf("operation size[%d] exceeds maximum operation size[%d]", len(operationBuffer), int(p.MaxOperationSize))
}
schema := &operationSchema{}
err := json.Unmarshal(operationBuffer, schema)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal operation buffer into operation schema: %s", err.Error())
}
var op *model.Operation
var parseErr error
switch schema.Operation {
case operation.TypeCreate:
op, parseErr = p.ParseCreateOperation(operationBuffer, batch)
case operation.TypeUpdate:
op, parseErr = p.ParseUpdateOperation(operationBuffer, batch)
case operation.TypeDeactivate:
op, parseErr = p.ParseDeactivateOperation(operationBuffer, batch)
case operation.TypeRecover:
op, parseErr = p.ParseRecoverOperation(operationBuffer, batch)
default:
return nil, fmt.Errorf("parse operation: operation type [%s] not supported", schema.Operation)
}
if parseErr != nil {
logger.Debug("Error parsing operation for batch", logfields.WithOperation(schema.Operation),
logfields.WithIsBatch(batch), logfields.WithError(parseErr))
return nil, parseErr
}
op.Namespace = namespace
op.ID = namespace + docutil.NamespaceDelimiter + op.UniqueSuffix
return op, nil
}
// operationSchema is used to get operation type.
type operationSchema struct {
// operation
Operation operation.Type `json:"type"`
}
type objectValidator struct {
}
func (ov *objectValidator) Validate(_ interface{}) error {
// default validator allows any anchor origin
return nil
}
type timeValidator struct {
}
func (tv *timeValidator) Validate(_, _ int64) error {
// default time validator allows any anchor time
return nil
}