sidetree 0.1.0 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/lib/sidetree/cas/fetch_result.rb +20 -0
- data/lib/sidetree/cas/ipfs.rb +101 -0
- data/lib/sidetree/cas.rb +6 -0
- data/lib/sidetree/did.rb +12 -14
- data/lib/sidetree/key.rb +53 -35
- data/lib/sidetree/model/cas_file_base.rb +40 -0
- data/lib/sidetree/model/chunk.rb +21 -0
- data/lib/sidetree/model/chunk_file.rb +72 -0
- data/lib/sidetree/model/core_index_file.rb +197 -0
- data/lib/sidetree/model/core_proof_file.rb +102 -0
- data/lib/sidetree/model/delta.rb +10 -1
- data/lib/sidetree/model/document.rb +10 -4
- data/lib/sidetree/model/provisional_index_file.rb +129 -0
- data/lib/sidetree/model/provisional_proof_file.rb +80 -0
- data/lib/sidetree/model/service.rb +6 -6
- data/lib/sidetree/model/suffix.rb +2 -2
- data/lib/sidetree/model.rb +11 -4
- data/lib/sidetree/op/create.rb +56 -5
- data/lib/sidetree/op/deactivate.rb +82 -0
- data/lib/sidetree/op/recover.rb +19 -0
- data/lib/sidetree/op/updatable.rb +98 -0
- data/lib/sidetree/op/update.rb +19 -0
- data/lib/sidetree/op.rb +20 -16
- data/lib/sidetree/util/anchored_data_serializer.rb +41 -0
- data/lib/sidetree/util/compressor.rb +38 -0
- data/lib/sidetree/util/jwk.rb +47 -0
- data/lib/sidetree/util/jws.rb +52 -0
- data/lib/sidetree/util.rb +8 -0
- data/lib/sidetree/validator.rb +51 -29
- data/lib/sidetree/version.rb +1 -1
- data/lib/sidetree.rb +36 -19
- data/sidetree.gemspec +3 -3
- metadata +37 -20
- data/exe/ion +0 -34
@@ -0,0 +1,197 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module Model
|
3
|
+
# https://identity.foundation/sidetree/spec/#core-index-file
|
4
|
+
class CoreIndexFile < CASFileBase
|
5
|
+
attr_reader :core_proof_file_uri
|
6
|
+
attr_reader :provisional_index_file_uri
|
7
|
+
attr_reader :writer_lock_id
|
8
|
+
attr_reader :create_ops
|
9
|
+
attr_reader :recover_ops
|
10
|
+
attr_reader :deactivate_ops
|
11
|
+
|
12
|
+
# @param [Array[Sidetree::OP::Create]] create_ops
|
13
|
+
# @param [Array[Sidetree::OP::Recover]] recover_ops
|
14
|
+
# @param [Array[Sidetree::OP::Deactivate]] deactivate_ops
|
15
|
+
# @param [String] provisional_index_file_uri
|
16
|
+
# @param [String] core_proof_file_uri
|
17
|
+
# @raise [Sidetree::Error]
|
18
|
+
def initialize(
|
19
|
+
create_ops: [],
|
20
|
+
recover_ops: [],
|
21
|
+
deactivate_ops: [],
|
22
|
+
provisional_index_file_uri: nil,
|
23
|
+
core_proof_file_uri: nil,
|
24
|
+
writer_lock_id: nil
|
25
|
+
)
|
26
|
+
(create_ops + recover_ops + deactivate_ops).each do |operation|
|
27
|
+
unless operation.is_a?(Sidetree::OP::Base)
|
28
|
+
raise Sidetree::Error, "Invalid operation class specified."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@create_ops = create_ops
|
32
|
+
@recover_ops = recover_ops
|
33
|
+
@deactivate_ops = deactivate_ops
|
34
|
+
@provisional_index_file_uri = provisional_index_file_uri
|
35
|
+
@core_proof_file_uri = core_proof_file_uri
|
36
|
+
@writer_lock_id = writer_lock_id
|
37
|
+
unless did_suffixes.length == did_suffixes.uniq.length
|
38
|
+
raise Sidetree::Error,
|
39
|
+
"Core index file multiple operations for the same DID"
|
40
|
+
end
|
41
|
+
if writer_lock_id &&
|
42
|
+
writer_lock_id.bytesize > Sidetree::Params::MAX_WRITER_LOCK_ID_SIZE
|
43
|
+
raise Sidetree::Error,
|
44
|
+
"Writer lock ID of #{writer_lock_id.bytesize} bytes exceeded the maximum size of #{Sidetree::Params::MAX_WRITER_LOCK_ID_SIZE} bytes"
|
45
|
+
end
|
46
|
+
if provisional_index_file_uri
|
47
|
+
Validator.validate_cas_file_uri!(
|
48
|
+
provisional_index_file_uri,
|
49
|
+
"provisional index file URI"
|
50
|
+
)
|
51
|
+
else
|
52
|
+
if (create_ops.length + recover_ops.length) > 0
|
53
|
+
raise Sidetree::Error, "Provisional index file uri missing"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if recover_ops.length > 0 || deactivate_ops.length > 0
|
57
|
+
Validator.validate_cas_file_uri!(
|
58
|
+
core_proof_file_uri,
|
59
|
+
"core proof file URI"
|
60
|
+
)
|
61
|
+
else
|
62
|
+
if core_proof_file_uri
|
63
|
+
raise Sidetree::Error,
|
64
|
+
"Core proof file is specified but there is no recover and no deactivate operation"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Parse core index file.
|
70
|
+
# @param [String] index_data core index file data.
|
71
|
+
# @param [Boolean] compressed Whether the index_data is compressed or not, default: true.
|
72
|
+
# @return [Sidetree::Model::ProvisionalIndexFile]
|
73
|
+
def self.parse(index_data, compressed: true)
|
74
|
+
decompressed =
|
75
|
+
(
|
76
|
+
if compressed
|
77
|
+
decompress(index_data, Sidetree::Params::MAX_CORE_INDEX_FILE_SIZE)
|
78
|
+
else
|
79
|
+
index_data
|
80
|
+
end
|
81
|
+
)
|
82
|
+
begin
|
83
|
+
json = JSON.parse(decompressed, symbolize_names: true)
|
84
|
+
create_ops, recover_ops, deactivate_ops = [], [], []
|
85
|
+
core_proof_uri, provisional_index_uri = nil, nil
|
86
|
+
json.each do |k, v|
|
87
|
+
case k
|
88
|
+
when :provisionalIndexFileUri
|
89
|
+
provisional_index_uri = v
|
90
|
+
when :coreProofFileUri
|
91
|
+
core_proof_uri = v
|
92
|
+
when :operations
|
93
|
+
create_ops, recover_ops, deactivate_ops = parse_operations(v)
|
94
|
+
when :writerLockId
|
95
|
+
unless v.is_a?(String)
|
96
|
+
raise Sidetree::Error, "Core index file writerLockId not string"
|
97
|
+
end
|
98
|
+
else
|
99
|
+
raise Sidetree::Error,
|
100
|
+
"Unexpected property #{k.to_s} in core index file"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
CoreIndexFile.new(
|
104
|
+
create_ops: create_ops,
|
105
|
+
recover_ops: recover_ops,
|
106
|
+
deactivate_ops: deactivate_ops,
|
107
|
+
provisional_index_file_uri: provisional_index_uri,
|
108
|
+
core_proof_file_uri: core_proof_uri,
|
109
|
+
writer_lock_id: json[:writerLockId]
|
110
|
+
)
|
111
|
+
rescue JSON::ParserError
|
112
|
+
raise Sidetree::Error, "Core index file is not json"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.parse_operations(operations)
|
117
|
+
create_ops, recover_ops, deactivate_ops = [], [], []
|
118
|
+
operations.each do |o_k, o_v|
|
119
|
+
case o_k
|
120
|
+
when :create
|
121
|
+
unless o_v.is_a?(Array)
|
122
|
+
raise Sidetree::Error, "Core index file create property not array"
|
123
|
+
end
|
124
|
+
create_ops =
|
125
|
+
o_v.map do |create|
|
126
|
+
Sidetree::OP::Create.from_json(create.to_json_c14n)
|
127
|
+
end
|
128
|
+
when :recover
|
129
|
+
unless o_v.is_a?(Array)
|
130
|
+
raise Sidetree::Error,
|
131
|
+
"Core index file recover property not array"
|
132
|
+
end
|
133
|
+
recover_ops =
|
134
|
+
o_v.map do |recover|
|
135
|
+
Sidetree::OP::Recover.from_json(recover.to_json_c14n)
|
136
|
+
end
|
137
|
+
when :deactivate
|
138
|
+
unless o_v.is_a?(Array)
|
139
|
+
raise Sidetree::Error,
|
140
|
+
"Core index file deactivate property not array"
|
141
|
+
end
|
142
|
+
deactivate_ops =
|
143
|
+
o_v.map do |deactivate|
|
144
|
+
Sidetree::OP::Deactivate.from_json(deactivate.to_json_c14n)
|
145
|
+
end
|
146
|
+
else
|
147
|
+
raise Sidetree::Error,
|
148
|
+
"Unexpected property #{o_k.to_s} in operations property in core index file"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
[create_ops, recover_ops, deactivate_ops]
|
152
|
+
end
|
153
|
+
|
154
|
+
private_class_method :parse_operations
|
155
|
+
|
156
|
+
def did_suffixes
|
157
|
+
create_ops.map { |o| o.suffix.unique_suffix } +
|
158
|
+
(recover_ops + deactivate_ops).map { |o| o.did_suffix }
|
159
|
+
end
|
160
|
+
|
161
|
+
# Build json string to be stored in CAS.
|
162
|
+
# @return [String] json string.
|
163
|
+
def to_json
|
164
|
+
params = {}
|
165
|
+
params[
|
166
|
+
:provisionalIndexFileUri
|
167
|
+
] = provisional_index_file_uri if provisional_index_file_uri
|
168
|
+
operations = {}
|
169
|
+
unless create_ops.empty?
|
170
|
+
operations[:create] = create_ops.map do |create|
|
171
|
+
{ suffixData: create.suffix.to_h }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
unless recover_ops.empty?
|
175
|
+
operations[:recover] = recover_ops.map do |recover|
|
176
|
+
{
|
177
|
+
didSuffix: recover.did_suffix,
|
178
|
+
revealValue: recover.revealed_value
|
179
|
+
}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
unless deactivate_ops.empty?
|
183
|
+
operations[:deactivate] = deactivate_ops.map do |deactivate|
|
184
|
+
{
|
185
|
+
didSuffix: deactivate.did_suffix,
|
186
|
+
revealValue: deactivate.revealed_value
|
187
|
+
}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
params[:operations] = operations
|
191
|
+
params[:coreProofFileUri] = core_proof_file_uri if core_proof_file_uri
|
192
|
+
params[:writerLockId] = writer_lock_id if writer_lock_id
|
193
|
+
params.to_json
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module Model
|
3
|
+
# https://identity.foundation/sidetree/spec/#core-proof-file
|
4
|
+
class CoreProofFile < CASFileBase
|
5
|
+
attr_reader :recover_proofs
|
6
|
+
attr_reader :deactivate_proofs
|
7
|
+
|
8
|
+
def initialize(recover_proofs, deactivate_proofs)
|
9
|
+
@recover_proofs = recover_proofs
|
10
|
+
@deactivate_proofs = deactivate_proofs
|
11
|
+
end
|
12
|
+
|
13
|
+
# Parse core proof file from compressed data.
|
14
|
+
# @param [String] proof_file compressed core proof file.
|
15
|
+
# @param [Boolean] compressed Whether the proof_file is compressed or not, default: true.
|
16
|
+
# @return [Sidetree::Model::CoreProofFile]
|
17
|
+
# @raise [Sidetree::Error]
|
18
|
+
def self.parse(proof_file, compressed: true)
|
19
|
+
decompressed =
|
20
|
+
(
|
21
|
+
if compressed
|
22
|
+
decompress(proof_file, Sidetree::Params::MAX_PROOF_FILE_SIZE)
|
23
|
+
else
|
24
|
+
proof_file
|
25
|
+
end
|
26
|
+
)
|
27
|
+
begin
|
28
|
+
json = JSON.parse(decompressed, symbolize_names: true)
|
29
|
+
recover_proofs, deactivate_proofs = [], []
|
30
|
+
json.keys.each do |k|
|
31
|
+
unless k == :operations
|
32
|
+
raise Sidetree::Error,
|
33
|
+
"Unexpected property #{k.to_s} in core proof file"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
unless json[:operations]
|
37
|
+
raise Sidetree::Error,
|
38
|
+
"Core proof file does not have any operation proofs"
|
39
|
+
end
|
40
|
+
json[:operations].keys.each do |k|
|
41
|
+
unless k == :recover || k == :deactivate
|
42
|
+
raise Sidetree::Error,
|
43
|
+
"Unexpected property #{k.to_s} in core proof file"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
if json[:operations][:recover]
|
47
|
+
unless json[:operations][:recover].is_a?(Array)
|
48
|
+
raise Sidetree::Error,
|
49
|
+
"Core proof file recover property not array"
|
50
|
+
end
|
51
|
+
recover_proofs =
|
52
|
+
json[:operations][:recover].each.map do |update|
|
53
|
+
update.keys.each do |k|
|
54
|
+
unless k == :signedData
|
55
|
+
raise Sidetree::Error,
|
56
|
+
"Unexpected property #{k.to_s} in core proof file"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
Sidetree::Util::JWS.parse(update[:signedData])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
if json[:operations][:deactivate]
|
63
|
+
unless json[:operations][:deactivate].is_a?(Array)
|
64
|
+
raise Sidetree::Error,
|
65
|
+
"Core proof file deactivate property not array"
|
66
|
+
end
|
67
|
+
deactivate_proofs =
|
68
|
+
json[:operations][:deactivate].each.map do |update|
|
69
|
+
update.keys.each do |k|
|
70
|
+
unless k == :signedData
|
71
|
+
raise Sidetree::Error,
|
72
|
+
"Unexpected property #{k.to_s} in core proof file"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
Sidetree::Util::JWS.parse(update[:signedData])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
if recover_proofs.length + deactivate_proofs.length == 0
|
79
|
+
raise Sidetree::Error, "Core proof file has no proof"
|
80
|
+
end
|
81
|
+
CoreProofFile.new(recover_proofs, deactivate_proofs)
|
82
|
+
rescue JSON::ParserError
|
83
|
+
raise Sidetree::Error, "Core proof file is not json"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Build json string to be stored in CAS.
|
88
|
+
# @return [String] json string.
|
89
|
+
def to_json
|
90
|
+
operations = {}
|
91
|
+
operations[:recover] = recover_proofs.map do |u|
|
92
|
+
{ signedData: u.to_s }
|
93
|
+
end unless recover_proofs.empty?
|
94
|
+
operations[:deactivate] = deactivate_proofs.map do |u|
|
95
|
+
{ signedData: u.to_s }
|
96
|
+
end unless deactivate_proofs.empty?
|
97
|
+
params = { operations: operations }
|
98
|
+
params.to_json
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/sidetree/model/delta.rb
CHANGED
@@ -3,6 +3,7 @@ module Sidetree
|
|
3
3
|
class Delta
|
4
4
|
attr_reader :patches, :update_commitment
|
5
5
|
|
6
|
+
# Initializer
|
6
7
|
# @param [Array[Hash]] patches
|
7
8
|
# @param [String] update_commitment
|
8
9
|
# @raise [Sidetree::Error]
|
@@ -11,7 +12,10 @@ module Sidetree
|
|
11
12
|
@update_commitment = update_commitment
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
+
# Create delta object from hash object.
|
16
|
+
# @param [Hash] object
|
17
|
+
# @return [Sidetree::Model::Delta]
|
18
|
+
def self.from_object(object)
|
15
19
|
Sidetree::Validator.validate_delta!(object)
|
16
20
|
Delta.new(object[:patches], object[:updateCommitment])
|
17
21
|
end
|
@@ -23,6 +27,11 @@ module Sidetree
|
|
23
27
|
def to_hash
|
24
28
|
Sidetree.to_hash(to_h)
|
25
29
|
end
|
30
|
+
|
31
|
+
def ==(other)
|
32
|
+
return false unless other.is_a?(Delta)
|
33
|
+
to_hash == other.to_hash
|
34
|
+
end
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
@@ -10,22 +10,22 @@ module Sidetree
|
|
10
10
|
def initialize(public_keys: [], services: [])
|
11
11
|
public_keys.each do |public_key|
|
12
12
|
unless public_key.is_a?(Sidetree::Key)
|
13
|
-
raise Error,
|
13
|
+
raise Error, "public_keys should be array of Sidetree::Key objects."
|
14
14
|
end
|
15
15
|
end
|
16
16
|
id_set = public_keys.map(&:id)
|
17
17
|
if (id_set.count - id_set.uniq.count) > 0
|
18
|
-
raise Error
|
18
|
+
raise Error "Public key id has to be unique."
|
19
19
|
end
|
20
20
|
services.each do |service|
|
21
21
|
unless service.is_a?(Sidetree::Model::Service)
|
22
22
|
raise Error,
|
23
|
-
|
23
|
+
"services should be array of Sidetree::Model::Service objects."
|
24
24
|
end
|
25
25
|
end
|
26
26
|
id_set = services.map(&:id)
|
27
27
|
if (id_set.count - id_set.uniq.count) > 0
|
28
|
-
raise Error
|
28
|
+
raise Error "Service id has to be unique."
|
29
29
|
end
|
30
30
|
|
31
31
|
@public_keys = public_keys
|
@@ -35,6 +35,12 @@ module Sidetree
|
|
35
35
|
def to_h
|
36
36
|
{ publicKeys: public_keys.map(&:to_h), services: services.map(&:to_h) }
|
37
37
|
end
|
38
|
+
|
39
|
+
# Generate replace patch.
|
40
|
+
# @return [Hash]
|
41
|
+
def to_replace_patch
|
42
|
+
{ action: OP::PatchAction::REPLACE, document: to_h }
|
43
|
+
end
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module Model
|
3
|
+
# https://identity.foundation/sidetree/spec/#provisional-index-file
|
4
|
+
class ProvisionalIndexFile < CASFileBase
|
5
|
+
attr_reader :provisional_proof_file_uri
|
6
|
+
attr_reader :chunks
|
7
|
+
attr_reader :operations
|
8
|
+
|
9
|
+
# Initialize
|
10
|
+
# @param [String] proof_file_uri Provisional Proof File URI.
|
11
|
+
# @param [Array[Sidetree::Model::Chunk]] chunks
|
12
|
+
# @param [Array[Sidetree::OP::Update]] operations Update operations
|
13
|
+
# @raise [Sidetree::Error]
|
14
|
+
def initialize(proof_file_uri: nil, chunks: [], operations: [])
|
15
|
+
if !chunks.is_a?(Array) || chunks.empty?
|
16
|
+
raise Sidetree::Error,
|
17
|
+
"Provisional Index File chunk property missing or incorrect type"
|
18
|
+
elsif chunks.length > 1
|
19
|
+
raise Sidetree::Error,
|
20
|
+
"Provisional Index File chunks does not have exactly one element"
|
21
|
+
end
|
22
|
+
@provisional_proof_file_uri = proof_file_uri
|
23
|
+
@chunks = chunks
|
24
|
+
@operations = operations
|
25
|
+
did_suffixes = operations.map(&:did_suffix).compact
|
26
|
+
if did_suffixes.length > 0
|
27
|
+
unless did_suffixes.length == did_suffixes.uniq.length
|
28
|
+
raise Sidetree::Error,
|
29
|
+
"Provisional Index File has multiple operations for same DID"
|
30
|
+
end
|
31
|
+
else
|
32
|
+
if proof_file_uri
|
33
|
+
raise Sidetree::Error,
|
34
|
+
"Provisional proof file '#{proof_file_uri}' not allowed in a provisional index file with no updates"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Parse provisional index file.
|
40
|
+
# @param [String] index_data provisional index file data.
|
41
|
+
# @param [Boolean] compressed Whether the +index_data+ is compressed or not, default: true.
|
42
|
+
# @return [Sidetree::Model::ProvisionalIndexFile]
|
43
|
+
def self.parse(index_data, compressed: true)
|
44
|
+
decompressed =
|
45
|
+
(
|
46
|
+
if compressed
|
47
|
+
decompress(
|
48
|
+
index_data,
|
49
|
+
Sidetree::Params::MAX_PROVISIONAL_INDEX_FILE_SIZE
|
50
|
+
)
|
51
|
+
else
|
52
|
+
index_data
|
53
|
+
end
|
54
|
+
)
|
55
|
+
begin
|
56
|
+
json = JSON.parse(decompressed, symbolize_names: true)
|
57
|
+
operations = []
|
58
|
+
chunks = []
|
59
|
+
json.each do |k, v|
|
60
|
+
case k
|
61
|
+
when :chunks
|
62
|
+
chunks =
|
63
|
+
v
|
64
|
+
.map do |chunk|
|
65
|
+
chunk.map do |c_k, c_v|
|
66
|
+
case c_k
|
67
|
+
when :chunkFileUri
|
68
|
+
Sidetree::Model::Chunk.new(c_v)
|
69
|
+
else
|
70
|
+
raise Sidetree::Error,
|
71
|
+
"Provisional Index File chunk has missing or unknown property"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
.flatten
|
76
|
+
when :provisionalProofFileUri
|
77
|
+
when :operations
|
78
|
+
v.each do |o_k, o_v|
|
79
|
+
case o_k
|
80
|
+
when :update
|
81
|
+
unless o_v.is_a?(Array)
|
82
|
+
raise Sidetree::Error,
|
83
|
+
"Provisional Index File update operation not array"
|
84
|
+
end
|
85
|
+
operations =
|
86
|
+
o_v.map do |update|
|
87
|
+
Sidetree::OP::Update.from_json(update.to_json_c14n)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
raise Sidetree::Error,
|
91
|
+
"Unexpected property '#{o_k.to_s}' in update operation"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
else
|
95
|
+
raise Sidetree::Error,
|
96
|
+
"Unexpected property #{k.to_s} in provisional index file"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
ProvisionalIndexFile.new(
|
100
|
+
chunks: chunks,
|
101
|
+
operations: operations,
|
102
|
+
proof_file_uri: json[:provisionalProofFileUri]
|
103
|
+
)
|
104
|
+
rescue JSON::ParserError
|
105
|
+
raise Sidetree::Error, "Provisional index file is not json"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Build json string to be stored in CAS.
|
110
|
+
# @return [String] json string.
|
111
|
+
def to_json
|
112
|
+
params = { chunks: chunks.map(&:to_h) }
|
113
|
+
unless operations.empty?
|
114
|
+
params[:operations] = {
|
115
|
+
update:
|
116
|
+
operations.map do |update|
|
117
|
+
{
|
118
|
+
didSuffix: update.did_suffix,
|
119
|
+
revealValue: update.revealed_value
|
120
|
+
}
|
121
|
+
end
|
122
|
+
}
|
123
|
+
params[:provisionalProofFileUri] = provisional_proof_file_uri
|
124
|
+
end
|
125
|
+
params.to_json
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module Model
|
3
|
+
# https://identity.foundation/sidetree/spec/#provisional-proof-file
|
4
|
+
class ProvisionalProofFile < CASFileBase
|
5
|
+
attr_reader :update_proofs
|
6
|
+
|
7
|
+
# Initialize
|
8
|
+
# @param [Array[JSON::JWS]] update_proofs Array of update proof.
|
9
|
+
def initialize(update_proofs)
|
10
|
+
@update_proofs = update_proofs
|
11
|
+
end
|
12
|
+
|
13
|
+
# Parse provisional proof file from compressed data.
|
14
|
+
# @param [String] proof_file compressed provisional proof file.
|
15
|
+
# @param [Boolean] compressed Whether the proof_file is compressed or not, default: true.
|
16
|
+
# @return [Sidetree::Model::ProvisionalProofFile]
|
17
|
+
# @raise [Sidetree::Error]
|
18
|
+
def self.parse(proof_file, compressed: true)
|
19
|
+
decompressed =
|
20
|
+
(
|
21
|
+
if compressed
|
22
|
+
decompress(proof_file, Sidetree::Params::MAX_PROOF_FILE_SIZE)
|
23
|
+
else
|
24
|
+
proof_file
|
25
|
+
end
|
26
|
+
)
|
27
|
+
begin
|
28
|
+
json = JSON.parse(decompressed, symbolize_names: true)
|
29
|
+
json.keys.each do |k|
|
30
|
+
unless k == :operations
|
31
|
+
raise Sidetree::Error,
|
32
|
+
"Unexpected property #{k.to_s} in provisional proof file"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
unless json[:operations]
|
36
|
+
raise Sidetree::Error,
|
37
|
+
"Provisional proof file does not have any operation proofs"
|
38
|
+
end
|
39
|
+
json[:operations].keys.each do |k|
|
40
|
+
unless k == :update
|
41
|
+
raise Sidetree::Error,
|
42
|
+
"Unexpected property #{k.to_s} in provisional proof file"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
unless json[:operations][:update].is_a?(Array)
|
46
|
+
raise Sidetree::Error,
|
47
|
+
"Provisional proof file update property not array"
|
48
|
+
end
|
49
|
+
update_proofs =
|
50
|
+
json[:operations][:update].each.map do |update|
|
51
|
+
update.keys.each do |k|
|
52
|
+
unless k == :signedData
|
53
|
+
raise Sidetree::Error,
|
54
|
+
"Unexpected property #{k.to_s} in provisional proof file"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
Sidetree::Util::JWS.parse(update[:signedData])
|
58
|
+
end
|
59
|
+
if update_proofs.empty?
|
60
|
+
raise Sidetree::Error, "Provisional proof file has no proof"
|
61
|
+
end
|
62
|
+
ProvisionalProofFile.new(update_proofs)
|
63
|
+
rescue JSON::ParserError
|
64
|
+
raise Sidetree::Error, "Provisional proof file is not json"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Build json string to be stored in CAS.
|
69
|
+
# @return [String] json string.
|
70
|
+
def to_json
|
71
|
+
params = {
|
72
|
+
operations: {
|
73
|
+
update: update_proofs.map { |u| { signedData: u.to_s } }
|
74
|
+
}
|
75
|
+
}
|
76
|
+
params.to_json
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -10,13 +10,13 @@ module Sidetree
|
|
10
10
|
# @raise [Sidetree::Error]
|
11
11
|
def initialize(id, type, endpoint)
|
12
12
|
Sidetree::Validator.validate_id!(id)
|
13
|
-
raise Error,
|
13
|
+
raise Error, "type should be String." unless type.is_a?(String)
|
14
14
|
if type.length > MAX_TYPE_LENGTH
|
15
15
|
raise Error,
|
16
16
|
"Service endpoint type length #{type.length} exceeds max allowed length of #{MAX_TYPE_LENGTH}."
|
17
17
|
end
|
18
18
|
if endpoint.is_a?(Array)
|
19
|
-
raise Error,
|
19
|
+
raise Error, "Service endpoint value cannot be an array."
|
20
20
|
end
|
21
21
|
|
22
22
|
Sidetree::Validator.validate_uri!(endpoint) if endpoint.is_a?(String)
|
@@ -33,16 +33,16 @@ module Sidetree
|
|
33
33
|
# @raise [Sidetree::Error]
|
34
34
|
# @return [Sidetree::Model::Service]
|
35
35
|
def self.from_hash(data)
|
36
|
-
Service.new(data[
|
36
|
+
Service.new(data["id"], data["type"], data["serviceEndpoint"])
|
37
37
|
end
|
38
38
|
|
39
39
|
# Convert data to Hash object.
|
40
40
|
# @return [Hash]
|
41
41
|
def to_h
|
42
42
|
hash = {}
|
43
|
-
hash[
|
44
|
-
hash[
|
45
|
-
hash[
|
43
|
+
hash["id"] = id if id
|
44
|
+
hash["type"] = type if type
|
45
|
+
hash["serviceEndpoint"] = endpoint if endpoint
|
46
46
|
hash
|
47
47
|
end
|
48
48
|
end
|
@@ -10,10 +10,10 @@ module Sidetree
|
|
10
10
|
@recovery_commitment = recovery_commitment
|
11
11
|
end
|
12
12
|
|
13
|
-
#
|
13
|
+
# Create Suffix object from hash object.
|
14
14
|
# @return [Sidetree::Model::Suffix]
|
15
15
|
# @raise [Sidetree::Error]
|
16
|
-
def self.
|
16
|
+
def self.from_object(object)
|
17
17
|
Sidetree::Validator.validate_suffix_data!(object)
|
18
18
|
Suffix.new(object[:deltaHash], object[:recoveryCommitment])
|
19
19
|
end
|
data/lib/sidetree/model.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
module Sidetree
|
2
2
|
module Model
|
3
|
-
autoload :Suffix,
|
4
|
-
autoload :Delta,
|
5
|
-
autoload :Document,
|
6
|
-
autoload :Service,
|
3
|
+
autoload :Suffix, "sidetree/model/suffix"
|
4
|
+
autoload :Delta, "sidetree/model/delta"
|
5
|
+
autoload :Document, "sidetree/model/document"
|
6
|
+
autoload :Service, "sidetree/model/service"
|
7
|
+
autoload :CASFileBase, "sidetree/model/cas_file_base"
|
8
|
+
autoload :CoreIndexFile, "sidetree/model/core_index_file"
|
9
|
+
autoload :ChunkFile, "sidetree/model/chunk_file"
|
10
|
+
autoload :Chunk, "sidetree/model/chunk"
|
11
|
+
autoload :ProvisionalIndexFile, "sidetree/model/provisional_index_file"
|
12
|
+
autoload :ProvisionalProofFile, "sidetree/model/provisional_proof_file"
|
13
|
+
autoload :CoreProofFile, "sidetree/model/core_proof_file"
|
7
14
|
end
|
8
15
|
end
|