sidetree 0.1.1 → 0.1.4
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/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 +2 -2
- data/lib/sidetree/key.rb +28 -10
- 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 +105 -0
- data/lib/sidetree/model/delta.rb +10 -1
- data/lib/sidetree/model/document.rb +6 -0
- data/lib/sidetree/model/provisional_index_file.rb +129 -0
- data/lib/sidetree/model/provisional_proof_file.rb +80 -0
- data/lib/sidetree/model/suffix.rb +2 -2
- data/lib/sidetree/model.rb +7 -0
- data/lib/sidetree/op/create.rb +36 -3
- data/lib/sidetree/op/deactivate.rb +73 -1
- data/lib/sidetree/op/recover.rb +11 -2
- data/lib/sidetree/op/updatable.rb +98 -0
- data/lib/sidetree/op/update.rb +19 -0
- data/lib/sidetree/op.rb +2 -0
- 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 +23 -0
- data/lib/sidetree/version.rb +1 -1
- data/lib/sidetree.rb +18 -1
- data/sidetree.gemspec +2 -1
- metadata +35 -4
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
|
@@ -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,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
@@ -4,5 +4,12 @@ module Sidetree
|
|
4
4
|
autoload :Delta, "sidetree/model/delta"
|
5
5
|
autoload :Document, "sidetree/model/document"
|
6
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
|
data/lib/sidetree/op/create.rb
CHANGED
@@ -29,6 +29,39 @@ module Sidetree
|
|
29
29
|
did.create_op
|
30
30
|
end
|
31
31
|
|
32
|
+
# Parse create operation data from json string
|
33
|
+
# @param [String] create_data create operation data(json string).
|
34
|
+
# @return [Sidetree::OP::Create]
|
35
|
+
# @raise [Sidetree::Error]
|
36
|
+
def self.from_json(create_data)
|
37
|
+
begin
|
38
|
+
json = JSON.parse(create_data, symbolize_names: true)
|
39
|
+
json.each do |k, v|
|
40
|
+
case k
|
41
|
+
when :type, :suffixData, :delta
|
42
|
+
else
|
43
|
+
raise Sidetree::Error,
|
44
|
+
"Create operation missing or unknown property"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
if json[:type] && json[:type] != Sidetree::OP::Type::CREATE
|
48
|
+
raise Sidetree::Error, "Create operation type incorrect"
|
49
|
+
end
|
50
|
+
suffix = Sidetree::Model::Suffix.from_object(json[:suffixData])
|
51
|
+
delta = nil
|
52
|
+
begin
|
53
|
+
# For compatibility with data pruning
|
54
|
+
delta = Sidetree::Model::Delta.from_object(json[:delta]) if json[
|
55
|
+
:delta
|
56
|
+
]
|
57
|
+
rescue Sidetree::Error
|
58
|
+
end
|
59
|
+
Create.new(suffix, delta)
|
60
|
+
rescue JSON::ParserError
|
61
|
+
raise Sidetree::Error, "create_data not json"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
32
65
|
def type
|
33
66
|
Sidetree::OP::Type::CREATE
|
34
67
|
end
|
@@ -53,8 +86,8 @@ module Sidetree
|
|
53
86
|
end
|
54
87
|
|
55
88
|
Create.new(
|
56
|
-
Sidetree::Model::Suffix.
|
57
|
-
Sidetree::Model::Delta.
|
89
|
+
Sidetree::Model::Suffix.from_object(json[:suffixData]),
|
90
|
+
Sidetree::Model::Delta.from_object(json[:delta])
|
58
91
|
)
|
59
92
|
rescue JSON::ParserError
|
60
93
|
raise Error, "Long form initial state should be encoded jcs."
|
@@ -62,7 +95,7 @@ module Sidetree
|
|
62
95
|
end
|
63
96
|
|
64
97
|
def to_h
|
65
|
-
{ suffixData: suffix.to_h, delta: delta
|
98
|
+
{ suffixData: suffix.to_h, delta: delta&.to_h }
|
66
99
|
end
|
67
100
|
|
68
101
|
# Generate long_suffix for DID.
|
@@ -1,7 +1,79 @@
|
|
1
1
|
module Sidetree
|
2
2
|
module OP
|
3
|
-
# Deactivate operation class
|
3
|
+
# Deactivate operation class
|
4
|
+
# https://identity.foundation/sidetree/spec/#deactivate
|
4
5
|
class Deactivate < Base
|
6
|
+
attr_reader :did_suffix
|
7
|
+
attr_reader :signed_data
|
8
|
+
attr_reader :revealed_value
|
9
|
+
|
10
|
+
# Initialize
|
11
|
+
# @param [String] did_suffix
|
12
|
+
# @param [JSON::JWS] signed_data
|
13
|
+
# @param [String] revealed_value
|
14
|
+
def initialize(did_suffix, signed_data, revealed_value)
|
15
|
+
Sidetree::Validator.validate_encoded_multi_hash!(
|
16
|
+
did_suffix,
|
17
|
+
"#{type} didSuffix"
|
18
|
+
)
|
19
|
+
Sidetree::Validator.validate_encoded_multi_hash!(
|
20
|
+
revealed_value,
|
21
|
+
"#{type} revealValue"
|
22
|
+
)
|
23
|
+
@did_suffix = did_suffix
|
24
|
+
@signed_data = signed_data
|
25
|
+
@revealed_value = revealed_value
|
26
|
+
end
|
27
|
+
|
28
|
+
# Parse Deactivate operation data from json string
|
29
|
+
# @param [String] deactivate_data deactivate operation data(json string).
|
30
|
+
# @return [Sidetree::OP::Deactivate]
|
31
|
+
# @raise [Sidetree::Error]
|
32
|
+
def self.from_json(deactivate_data)
|
33
|
+
begin
|
34
|
+
json = JSON.parse(deactivate_data, symbolize_names: true)
|
35
|
+
jws, revealed_value, did_suffix = nil, nil, nil
|
36
|
+
json.each do |k, v|
|
37
|
+
case k
|
38
|
+
when :type
|
39
|
+
unless v == Sidetree::OP::Type::DEACTIVATE
|
40
|
+
raise Sidetree::Error, "Deactivate operation type incorrect"
|
41
|
+
end
|
42
|
+
when :didSuffix
|
43
|
+
did_suffix = v
|
44
|
+
when :revealValue
|
45
|
+
revealed_value = v
|
46
|
+
when :signedData
|
47
|
+
jws = Sidetree::Util::JWS.parse(v)
|
48
|
+
unless jws.keys.length == 2
|
49
|
+
raise Sidetree::Error,
|
50
|
+
"Deactivate operation signed data missing or unknown property"
|
51
|
+
end
|
52
|
+
Sidetree::Util::JWK.validate!(
|
53
|
+
Sidetree::Util::JWK.parse(jws["recoveryKey"])
|
54
|
+
)
|
55
|
+
else
|
56
|
+
raise Sidetree::Error,
|
57
|
+
"Unexpected property #{k.to_s} in deactivate operation"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
if jws
|
61
|
+
Validator.validate_canonicalize_object_hash!(
|
62
|
+
jws["recoveryKey"],
|
63
|
+
revealed_value,
|
64
|
+
"Deactivate key"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
unless did_suffix
|
68
|
+
raise Sidetree::Error, "The deactivate didSuffix must be a string"
|
69
|
+
end
|
70
|
+
Deactivate.new(did_suffix, jws, revealed_value)
|
71
|
+
rescue JSON::ParserError
|
72
|
+
raise Sidetree::Error, "deactivate_data not json"
|
73
|
+
rescue JSON::JWS::InvalidFormat
|
74
|
+
raise Sidetree::Error, "Invalid signedData"
|
75
|
+
end
|
76
|
+
end
|
5
77
|
def type
|
6
78
|
Sidetree::OP::Type::DEACTIVATE
|
7
79
|
end
|
data/lib/sidetree/op/recover.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
module Sidetree
|
2
2
|
module OP
|
3
|
-
# Recover operation class.
|
4
|
-
|
3
|
+
# Recover operation class.
|
4
|
+
# https://identity.foundation/sidetree/spec/#recover
|
5
|
+
class Recover < Updatable
|
6
|
+
# Parse Recover operation data from json string
|
7
|
+
# @param [String] recover_data recover operation data(json string).
|
8
|
+
# @return [Sidetree::OP::Recover]
|
9
|
+
# @raise [Sidetree::Error]
|
10
|
+
def self.from_json(recover_data)
|
11
|
+
parse_json(recover_data, Sidetree::OP::Type::RECOVER)
|
12
|
+
end
|
13
|
+
|
5
14
|
def type
|
6
15
|
Sidetree::OP::Type::RECOVER
|
7
16
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module OP
|
3
|
+
class Updatable < Base
|
4
|
+
attr_reader :did_suffix
|
5
|
+
attr_reader :delta
|
6
|
+
attr_reader :signed_data
|
7
|
+
attr_reader :revealed_value
|
8
|
+
|
9
|
+
# Initialize
|
10
|
+
# @param [String] did_suffix
|
11
|
+
# @param [Sidetree::Model::Delta] delta
|
12
|
+
# @param [JSON::JWS] signed_data
|
13
|
+
# @param [String] revealed_value
|
14
|
+
# @raise [Sidetree::Error]
|
15
|
+
def initialize(did_suffix, delta, signed_data, revealed_value)
|
16
|
+
Sidetree::Validator.validate_encoded_multi_hash!(
|
17
|
+
did_suffix,
|
18
|
+
"#{type} didSuffix"
|
19
|
+
)
|
20
|
+
Sidetree::Validator.validate_encoded_multi_hash!(
|
21
|
+
revealed_value,
|
22
|
+
"#{type} revealValue"
|
23
|
+
)
|
24
|
+
if signed_data
|
25
|
+
Validator.validate_canonicalize_object_hash!(
|
26
|
+
signed_data[key_name],
|
27
|
+
revealed_value,
|
28
|
+
type
|
29
|
+
)
|
30
|
+
end
|
31
|
+
@did_suffix = did_suffix
|
32
|
+
@delta = delta
|
33
|
+
@signed_data = signed_data
|
34
|
+
@revealed_value = revealed_value
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_json(data, type)
|
38
|
+
begin
|
39
|
+
json = JSON.parse(data, symbolize_names: true)
|
40
|
+
delta, jws, revealed_value, did_suffix = nil, nil, nil, nil
|
41
|
+
json.each do |k, v|
|
42
|
+
case k
|
43
|
+
when :type
|
44
|
+
unless v == type
|
45
|
+
raise Sidetree::Error,
|
46
|
+
"#{type.capitalize} operation type incorrect"
|
47
|
+
end
|
48
|
+
when :didSuffix
|
49
|
+
did_suffix = v
|
50
|
+
when :revealValue
|
51
|
+
revealed_value = v
|
52
|
+
when :signedData
|
53
|
+
jws = Sidetree::Util::JWS.parse(v)
|
54
|
+
unless jws.keys.length ==
|
55
|
+
(type == Sidetree::OP::Type::RECOVER ? 3 : 2)
|
56
|
+
raise Sidetree::Error,
|
57
|
+
"#{type.capitalize} operation signed data missing or unknown property"
|
58
|
+
end
|
59
|
+
key_name =
|
60
|
+
(
|
61
|
+
if type == Sidetree::OP::Type::RECOVER
|
62
|
+
"recoveryKey"
|
63
|
+
else
|
64
|
+
"updateKey"
|
65
|
+
end
|
66
|
+
)
|
67
|
+
Sidetree::Util::JWK.validate!(
|
68
|
+
Sidetree::Util::JWK.parse(jws[key_name])
|
69
|
+
)
|
70
|
+
when :delta
|
71
|
+
delta = Sidetree::Model::Delta.from_object(v)
|
72
|
+
else
|
73
|
+
raise Sidetree::Error,
|
74
|
+
"Unexpected property #{k.to_s} in #{type} operation"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
unless did_suffix
|
78
|
+
raise Sidetree::Error, "The #{type} didSuffix must be a string"
|
79
|
+
end
|
80
|
+
Module.const_get("Sidetree::OP::#{type.capitalize}").new(
|
81
|
+
did_suffix,
|
82
|
+
delta,
|
83
|
+
jws,
|
84
|
+
revealed_value
|
85
|
+
)
|
86
|
+
rescue JSON::ParserError
|
87
|
+
raise Sidetree::Error, "data dose not json"
|
88
|
+
rescue JSON::JWS::InvalidFormat
|
89
|
+
raise Sidetree::Error, "Invalid signedData"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def key_name
|
94
|
+
type == Sidetree::OP::Type::UPDATE ? "updateKey" : "recoveryKey"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module OP
|
3
|
+
# Update operation class.
|
4
|
+
# https://identity.foundation/sidetree/spec/#update
|
5
|
+
class Update < Updatable
|
6
|
+
# Parse update operation data from json string
|
7
|
+
# @param [String] update_data update operation data(json string).
|
8
|
+
# @return [Sidetree::OP::Update]
|
9
|
+
# @raise [Sidetree::Error]
|
10
|
+
def self.from_json(update_data)
|
11
|
+
parse_json(update_data, Sidetree::OP::Type::UPDATE)
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
Sidetree::OP::Type::UPDATE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/sidetree/op.rb
CHANGED
@@ -32,8 +32,10 @@ module Sidetree
|
|
32
32
|
end
|
33
33
|
|
34
34
|
autoload :Base, "sidetree/op/base"
|
35
|
+
autoload :Updatable, "sidetree/op/updatable"
|
35
36
|
autoload :Create, "sidetree/op/create"
|
36
37
|
autoload :Recover, "sidetree/op/recover"
|
38
|
+
autoload :Update, "sidetree/op/update"
|
37
39
|
autoload :Deactivate, "sidetree/op/deactivate"
|
38
40
|
end
|
39
41
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Sidetree
|
2
|
+
module Util
|
3
|
+
module AnchoredDataSerializer
|
4
|
+
DELIMITER = "."
|
5
|
+
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Serialize given data as Anchor String.
|
9
|
+
# @param [Integer] op_count Number of operations
|
10
|
+
# @param [String] uri Core Index File uri.
|
11
|
+
# @return [String] Anchor String
|
12
|
+
# @raise [Sidetree::Error]
|
13
|
+
def serialize(op_count, uri)
|
14
|
+
if op_count > Sidetree::Params::MAX_OPERATION_COUNT
|
15
|
+
raise Sidetree::Error, "Number of operations greater than max"
|
16
|
+
end
|
17
|
+
"#{op_count}#{DELIMITER}#{uri}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# Deserializes the given string that is read from the blockchain into data.
|
21
|
+
# @param [String] anchor_str Anchor String
|
22
|
+
# @return [Array[Integer, String]]
|
23
|
+
# @raise [Sidetree::Error]
|
24
|
+
def deserialize(anchor_str)
|
25
|
+
data = anchor_str.split(DELIMITER)
|
26
|
+
raise Sidetree::Error, "Invalid anchor string" unless data.length == 2
|
27
|
+
unless data[0] =~ /^[1-9]\d*$/
|
28
|
+
raise Sidetree::Error,
|
29
|
+
"Number of operations in anchor string is not positive number"
|
30
|
+
end
|
31
|
+
|
32
|
+
count = data[0].to_i
|
33
|
+
if count > Sidetree::Params::MAX_OPERATION_COUNT
|
34
|
+
raise Sidetree::Error,
|
35
|
+
"Number of operations in anchor string greater than max"
|
36
|
+
end
|
37
|
+
[count, data[1]]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "zlib"
|
2
|
+
|
3
|
+
module Sidetree
|
4
|
+
module Util
|
5
|
+
module Compressor
|
6
|
+
# The estimated ratio/multiplier of decompressed Sidetree CAS file size compared against the compressed file size.
|
7
|
+
ESTIMATE_DECOMPRESSION_MULTIPLIER = 3
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
# Compresses teh data in gzip and return it as buffer.
|
12
|
+
# @param [String] data Data to be compressed.
|
13
|
+
# @return [String] compressed data.
|
14
|
+
def compress(data)
|
15
|
+
io = StringIO.new("w")
|
16
|
+
Zlib::GzipWriter.wrap(io) do |w|
|
17
|
+
w.mtime = 0
|
18
|
+
w.write data
|
19
|
+
end
|
20
|
+
io.string.force_encoding("binary")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Decompresses +compressed+.
|
24
|
+
# @param [String] compressed compressed data.
|
25
|
+
# @return [String] decompressed data.
|
26
|
+
# @raise [Sidetree::Error] raise if data exceeds max_bytes size.
|
27
|
+
def decompress(compressed, max_bytes: nil)
|
28
|
+
if max_bytes && compressed.bytesize > max_bytes
|
29
|
+
raise Sidetree::Error, "Exceed maximum compressed chunk file size."
|
30
|
+
end
|
31
|
+
io = StringIO.new(compressed)
|
32
|
+
result = StringIO.new
|
33
|
+
Zlib::GzipReader.wrap(io) { |gz| result << gz.read }
|
34
|
+
result.string
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|