sidetree 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|