scale.rb 0.2.10 → 0.2.16

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.
data/Cargo.toml CHANGED
@@ -2,10 +2,17 @@
2
2
  name = "SCALE-testing-interface"
3
3
  version = "0.1.0"
4
4
  authors = ["Robert Hambrock <robert@web3.foundation>"]
5
+ edition = "2018"
5
6
 
6
7
  [dependencies]
7
- parity-scale-codec = { version = "1.2.0" }
8
+ parity-scale-codec = { version = "1.3.6" }
9
+ sp-core = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", features = ["full_crypto"]}
10
+ frame-support = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git" }
8
11
 
9
12
  [lib]
10
13
  name = "scale_ffi"
11
- crate-type = ["dylib"]
14
+ crate-type = ["dylib"]
15
+
16
+ [[bin]]
17
+ name = "storage_key"
18
+ path = "src/storage_key.rs"
data/Gemfile.lock CHANGED
@@ -1,32 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- scale.rb (0.2.10)
5
- activesupport (>= 4.0.0)
4
+ scale.rb (0.2.16)
6
5
  json (~> 2.3.0)
7
- substrate_common.rb (~> 0.1.9)
6
+ kontena-websocket-client (~> 0.1.1)
7
+ substrate_common.rb (~> 0.1.10)
8
8
  thor (~> 0.19.0)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- activesupport (6.0.3.4)
14
- concurrent-ruby (~> 1.0, >= 1.0.2)
15
- i18n (>= 0.7, < 2)
16
- minitest (~> 5.1)
17
- tzinfo (~> 1.1)
18
- zeitwerk (~> 2.2, >= 2.2.2)
19
13
  base58 (0.2.3)
20
14
  blake2b (0.10.0)
21
15
  coderay (1.1.2)
22
- concurrent-ruby (1.1.7)
23
16
  diff-lcs (1.3)
24
17
  ffi (1.12.2)
25
- i18n (1.8.5)
26
- concurrent-ruby (~> 1.0)
27
18
  json (2.3.1)
19
+ kontena-websocket-client (0.1.1)
20
+ websocket-driver (~> 0.6.5)
28
21
  method_source (0.9.2)
29
- minitest (5.14.2)
30
22
  pry (0.12.2)
31
23
  coderay (~> 1.1.0)
32
24
  method_source (~> 0.9.0)
@@ -44,16 +36,15 @@ GEM
44
36
  diff-lcs (>= 1.2.0, < 2.0)
45
37
  rspec-support (~> 3.9.0)
46
38
  rspec-support (3.9.0)
47
- substrate_common.rb (0.1.9)
39
+ substrate_common.rb (0.1.10)
48
40
  base58
49
41
  blake2b
50
42
  xxhash
51
43
  thor (0.19.4)
52
- thread_safe (0.3.6)
53
- tzinfo (1.2.7)
54
- thread_safe (~> 0.1)
44
+ websocket-driver (0.6.5)
45
+ websocket-extensions (>= 0.1.0)
46
+ websocket-extensions (0.1.5)
55
47
  xxhash (0.4.0)
56
- zeitwerk (2.4.1)
57
48
 
58
49
  PLATFORMS
59
50
  ruby
data/exe/scale CHANGED
@@ -75,6 +75,15 @@ class ScaleCli < Thor
75
75
  type = Scale::Types.get(type_name)
76
76
  p "0x" + type.new(value.to_i).encode
77
77
  end
78
+
79
+ desc "check_read_proof ROOT PROOF STORAGE_KEY", "check read proof and output leaf's value"
80
+ def check_read_proof(root, proof, storage_key)
81
+ Scale::TypeRegistry.instance.load
82
+ proof = proof.split(",").map(&:strip)
83
+
84
+ value = Scale::Types::TrieNode::check(root, proof, storage_key)
85
+ p value
86
+ end
78
87
  end
79
88
 
80
89
  ScaleCli.start(ARGV)
data/lib/helper.rb ADDED
@@ -0,0 +1,126 @@
1
+
2
+ class SubstrateClient::Helper
3
+ class << self
4
+ def generate_storage_key_from_metadata(metadata, module_name, storage_name, params = nil)
5
+ # find the storage item from metadata
6
+ metadata_modules = metadata.value.value[:metadata][:modules]
7
+ metadata_module = metadata_modules.detect { |mm| mm[:name] == module_name }
8
+ raise "Module '#{module_name}' not exist" unless metadata_module
9
+ storage_item = metadata_module[:storage][:items].detect { |item| item[:name] == storage_name }
10
+ raise "Storage item '#{storage_name}' not exist. \n#{metadata_module.inspect}" unless storage_item
11
+
12
+ if storage_item[:type][:Plain]
13
+ return_type = storage_item[:type][:Plain]
14
+ elsif map = storage_item[:type][:Map]
15
+ raise "Storage call of type \"Map\" requires 1 parameter" if params.nil? || params.length != 1
16
+
17
+ hasher = map[:hasher]
18
+ return_type = map[:value]
19
+ # TODO: decode to account id if param is address
20
+ # params[0] = decode(params[0]) if map[:key] == "AccountId"
21
+ type = Scale::Types.get(map[:key])
22
+ params[0] = type.new(params[0]).encode
23
+ elsif map = storage_item[:type][:DoubleMap]
24
+ raise "Storage call of type \"DoubleMapType\" requires 2 parameters" if params.nil? || params.length != 2
25
+
26
+ hasher = map[:hasher]
27
+ hasher2 = map[:key2Hasher]
28
+ return_type = map[:value]
29
+ params[0] = Scale::Types.get(map[:key1]).new(params[0]).encode
30
+ params[1] = Scale::Types.get(map[:key2]).new(params[1]).encode
31
+ else
32
+ raise NotImplementedError
33
+ end
34
+
35
+ storage_prefix = metadata_module[:storage][:prefix]
36
+ storage_key = generate_storage_key(
37
+ storage_prefix.nil? ? module_name : storage_prefix,
38
+ storage_name,
39
+ params,
40
+ hasher,
41
+ hasher2,
42
+ metadata.value.value[:metadata][:version]
43
+ )
44
+ [storage_key, return_type]
45
+ end
46
+
47
+ def generate_storage_key(module_name, storage_name, params = nil, hasher = nil, hasher2 = nil, metadata_version = nil)
48
+ metadata_version = 12 if metadata_version.nil?
49
+ if metadata_version and metadata_version >= 9
50
+ storage_key = Crypto.twox128(module_name) + Crypto.twox128(storage_name)
51
+
52
+ params&.each_with_index do |param, index|
53
+ if index == 0
54
+ param_hasher = hasher
55
+ elsif index == 1
56
+ param_hasher = hasher2
57
+ else
58
+ raise "Unexpected third parameter for storage call"
59
+ end
60
+
61
+ param_key = if param.class == String && param.start_with?("0x")
62
+ param.hex_to_bytes
63
+ else
64
+ param.encode().hex_to_bytes
65
+ end
66
+ param_hasher = "Twox128" if param_hasher.nil?
67
+ storage_key += Crypto.send(param_hasher.underscore, param_key)
68
+ end
69
+
70
+ "0x#{storage_key}"
71
+ else
72
+ # TODO: add test
73
+ storage_key = module_name + " " + storage_name
74
+
75
+ unless params.nil?
76
+ params = [params] if params.class != ::Array
77
+ params_key = params.join("")
78
+ hasher = "Twox128" if hasher.nil?
79
+ storage_key += params_key.hex_to_bytes.bytes_to_utf8
80
+ end
81
+
82
+ "0x#{Crypto.send( hasher.underscore, storage_key )}"
83
+ end
84
+ end
85
+
86
+ def compose_call_from_metadata(metadata, module_name, call_name, params)
87
+ call = metadata.get_module_call(module_name, call_name)
88
+
89
+ value = {
90
+ call_index: call[:lookup],
91
+ module_name: module_name,
92
+ call_name: call_name,
93
+ params: []
94
+ }
95
+
96
+ params.keys.each_with_index do |call_param_name, i|
97
+ param_value = params[call_param_name]
98
+ value[:params] << {
99
+ name: call_param_name.to_s,
100
+ type: call[:args][i][:type],
101
+ value: param_value
102
+ }
103
+ end
104
+
105
+ Scale::Types::Extrinsic.new(value).encode
106
+ end
107
+
108
+ def decode_block(block)
109
+ block["block"]["header"]["number"] = block["block"]["header"]["number"].to_i(16)
110
+
111
+ block["block"]["extrinsics"].each_with_index do |hex, i|
112
+ scale_bytes = Scale::Bytes.new(hex)
113
+ block["block"]["extrinsics"][i] = Scale::Types::Extrinsic.decode(scale_bytes).to_human
114
+ end
115
+
116
+ block['block']['header']["digest"]["logs"].each_with_index do |hex, i|
117
+ scale_bytes = Scale::Bytes.new(hex)
118
+ log = Scale::Types::LogDigest.decode(scale_bytes).to_human
119
+ block['block']['header']["digest"]["logs"][i] = log
120
+ end
121
+
122
+ block
123
+ end
124
+
125
+ end
126
+ end
@@ -35,6 +35,16 @@ module Scale
35
35
  end
36
36
  end
37
37
  end
38
+
39
+ def get_module_storage(module_name, storage_name)
40
+ the_module = get_module(module_name)
41
+ if the_module[:storage].class == Array
42
+ storages = the_module[:storage]
43
+ else
44
+ storages = the_module[:storage][:items]
45
+ end
46
+ storages.find {|storage| storage[:name] == storage_name}
47
+ end
38
48
  end
39
49
 
40
50
  class MetadataModule
@@ -68,6 +78,10 @@ module Scale
68
78
 
69
79
  MetadataModule.new(result)
70
80
  end
81
+
82
+ def get_storage(storage_name)
83
+ self.value[:storage].find { |storage| storage[:name].downcase == storage_name.downcase }
84
+ end
71
85
  end
72
86
 
73
87
  class MetadataModuleStorage
@@ -26,21 +26,19 @@ module Scale
26
26
  call_module_index = 0
27
27
  event_module_index = 0
28
28
 
29
- modules.map(&:value).each do |m|
29
+ modules.map(&:value).each_with_index do |m, module_index|
30
30
  if m[:calls]
31
31
  m[:calls].each_with_index do |call, index|
32
- call[:lookup] = "%02x%02x" % [call_module_index, index]
32
+ call[:lookup] = "%02x%02x" % [module_index, index]
33
33
  result.call_index[call[:lookup]] = [m, call]
34
34
  end
35
- call_module_index += 1
36
35
  end
37
36
 
38
37
  if m[:events]
39
38
  m[:events].each_with_index do |event, index|
40
- event[:lookup] = "%02x%02x" % [event_module_index, index]
39
+ event[:lookup] = "%02x%02x" % [module_index, index]
41
40
  result.event_index[event[:lookup]] = [m, event]
42
41
  end
43
- event_module_index += 1
44
42
  end
45
43
  end
46
44
 
data/lib/scale.rb CHANGED
@@ -2,13 +2,12 @@ require "scale/version"
2
2
 
3
3
  require "substrate_common"
4
4
  require "json"
5
- require "active_support"
6
- require "active_support/core_ext/string"
7
5
  require "singleton"
8
6
 
9
7
  require "scale/base"
10
8
  require "scale/types"
11
9
  require "scale/block"
10
+ require "scale/trie"
12
11
 
13
12
  require "metadata/metadata"
14
13
  require "metadata/metadata_v0"
@@ -25,13 +24,41 @@ require "metadata/metadata_v10"
25
24
  require "metadata/metadata_v11"
26
25
  require "metadata/metadata_v12"
27
26
 
27
+ require "substrate_client"
28
+ require "logger"
29
+ require "helper"
30
+ require 'kontena-websocket-client'
31
+
32
+ class String
33
+ def upcase_first
34
+ self.sub(/\S/, &:upcase)
35
+ end
36
+
37
+ def camelize
38
+ self.split('_').collect(&:upcase_first).join
39
+ end
40
+
41
+ def underscore
42
+ self.gsub(/::/, '/').
43
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
44
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
45
+ tr("-", "_").
46
+ downcase
47
+ end
48
+ end
49
+
28
50
  module Scale
29
51
  class Error < StandardError; end
30
52
 
31
53
  class TypeRegistry
32
54
  include Singleton
33
- attr_reader :spec_name, :types, :versioning, :custom_types
34
- attr_accessor :spec_version
55
+
56
+ # init by load, and will not change
57
+ attr_reader :spec_name, :types
58
+ attr_reader :versioning, :custom_types # optional
59
+
60
+ # will change by different spec version
61
+ attr_accessor :spec_version # optional
35
62
  attr_accessor :metadata
36
63
 
37
64
  def load(spec_name: nil, custom_types: nil)
@@ -42,13 +69,18 @@ module Scale
42
69
 
43
70
  default_types, _, _ = load_chain_spec_types("default")
44
71
 
45
- if spec_name.nil? || spec_name == "default"
72
+ if spec_name
73
+ begin
74
+ @spec_name = spec_name
75
+ spec_types, @versioning, @spec_version = load_chain_spec_types(spec_name)
76
+ @types = default_types.merge(spec_types)
77
+ rescue => ex
78
+ puts "There is no types json file named #{spec_name}"
79
+ @types = default_types
80
+ end
81
+ else
46
82
  @spec_name = "default"
47
83
  @types = default_types
48
- else
49
- @spec_name = spec_name
50
- spec_types, @versioning, @spec_version = load_chain_spec_types(spec_name)
51
- @types = default_types.merge(spec_types)
52
84
  end
53
85
 
54
86
  self.custom_types = custom_types
@@ -62,7 +94,8 @@ module Scale
62
94
 
63
95
  if @spec_version && @versioning
64
96
  @versioning.each do |item|
65
- if @spec_version >= item["runtime_range"][0] && @spec_version <= (item["runtime_range"][1] || 1073741823)
97
+ if @spec_version >= item["runtime_range"][0] &&
98
+ ( item["runtime_range"][1].nil? || @spec_version <= item["runtime_range"][1] )
66
99
  all_types.merge!(item["types"])
67
100
  end
68
101
  end
@@ -244,7 +277,7 @@ module Scale
244
277
  include Scale::Types.type_of(type_str)
245
278
  inner_type inner_type_str
246
279
  end
247
- name = "#{type_str}_Of_#{inner_type_str.camelize}_#{klass.object_id}"
280
+ name = "#{type_str}<#{inner_type_str.camelize}>_#{klass.object_id}"
248
281
  Scale::Types.const_set fix(name), klass
249
282
  else
250
283
  raise "#{type_str} not support inner type: #{type_string}"
@@ -341,7 +374,7 @@ def rename(type)
341
374
  return "AccountData" if type == "AccountData<Balance>"
342
375
 
343
376
  if type =~ /\[U\d+; \d+\]/
344
- byte_length = type.scan(/\[U\d+; (\d+)\]/).first.first
377
+ byte_length = type.scan(/\[U\d+; (\d+)\]/).first.first.to_i
345
378
  return "VecU8Length#{byte_length}"
346
379
  end
347
380
 
data/lib/scale/base.rb CHANGED
@@ -349,9 +349,8 @@ module Scale
349
349
 
350
350
  module ClassMethods
351
351
  def decode(scale_bytes)
352
- byte_length = name[25..]
353
- raise "length is wrong: #{byte_length}" unless %w[2 3 4 8 16 20 32 64].include?(byte_length)
354
- byte_length = byte_length.to_i
352
+ byte_length = self::BYTE_LENGTH
353
+ raise "#{self.name} byte length is wrong: #{byte_length}" unless %w[2 3 4 8 16 20 32 64 128 256].include?(byte_length.to_s)
355
354
 
356
355
  bytes = scale_bytes.get_next_bytes(byte_length)
357
356
  str = bytes.pack("C*").force_encoding("utf-8")
@@ -368,12 +367,10 @@ module Scale
368
367
  end
369
368
 
370
369
  def encode
371
- class_name = self.class.to_s
372
- length = class_name[25..]
373
- raise "length is wrong: #{length}" unless %w[2 3 4 8 16 20 32 64].include?(length)
374
- length = length.to_i
370
+ byte_length = self.class::BYTE_LENGTH
371
+ raise "#{self.class.name}'s byte length is wrong: #{byte_length}" unless %w[2 3 4 8 16 20 32 64 128 256].include?(byte_length.to_s)
375
372
 
376
- if value.start_with?("0x") && value.length == (length * 2 + 2)
373
+ if value.start_with?("0x") && value.length == (byte_length * 2 + 2)
377
374
  value[2..]
378
375
  else
379
376
  bytes = value.unpack("C*")
data/lib/scale/block.rb CHANGED
@@ -10,7 +10,7 @@ module Scale
10
10
  end
11
11
 
12
12
  def self.decode(scale_bytes)
13
- metadata = Scale::TypeRegistry.instance.metadata
13
+ metadata = Scale::TypeRegistry.instance.metadata.value
14
14
  result = {}
15
15
 
16
16
  extrinsic_length = Compact.decode(scale_bytes).value
@@ -24,9 +24,6 @@ module Scale
24
24
  if contains_transaction
25
25
  address = Scale::Types.get("Address").decode(scale_bytes)
26
26
  result[:address] = address.value
27
- result[:account_length] = address.account_length
28
- result[:account_id] = address.account_id
29
- result[:account_index] = address.account_index
30
27
  result[:signature] = Scale::Types.get("Signature").decode(scale_bytes).value
31
28
  result[:nonce] = Scale::Types.get("Compact").decode(scale_bytes).value
32
29
  result[:era] = Scale::Types.get("Era").decode(scale_bytes).value
@@ -39,9 +36,6 @@ module Scale
39
36
  if contains_transaction
40
37
  address = Scale::Types.get("Address").decode(scale_bytes)
41
38
  result[:address] = address.value
42
- result[:account_length] = address.account_length
43
- result[:account_id] = address.account_id
44
- result[:account_index] = address.account_index
45
39
  result[:signature] = Scale::Types.get("Signature").decode(scale_bytes).value
46
40
  result[:era] = Scale::Types.get("Era").decode(scale_bytes).value
47
41
  result[:nonce] = Scale::Types.get("Compact").decode(scale_bytes).value
@@ -55,9 +49,6 @@ module Scale
55
49
  if contains_transaction
56
50
  address = Scale::Types.get("Address").decode(scale_bytes)
57
51
  result[:address] = address.value
58
- result[:account_length] = address.account_length
59
- result[:account_id] = address.account_id
60
- result[:account_index] = address.account_index
61
52
  result[:signature] = Scale::Types.get("Signature").decode(scale_bytes).value
62
53
  result[:era] = Scale::Types.get("Era").decode(scale_bytes).value
63
54
  result[:nonce] = Scale::Types.get("Compact").decode(scale_bytes).value
@@ -69,16 +60,11 @@ module Scale
69
60
  elsif version_info == "0x04" || version_info == "0x84"
70
61
 
71
62
  if contains_transaction
72
- address = Scale::Types.get("Address").decode(scale_bytes)
73
- result[:address] = address.value
74
- result[:account_length] = address.account_length
75
- result[:account_id] = address.account_id
76
- result[:account_index] = address.account_index
77
- result[:signature_version] = Scale::Types.get("U8").decode(scale_bytes).value
78
- result[:signature] = Scale::Types.get("Signature").decode(scale_bytes).value
79
- result[:era] = Scale::Types.get("Era").decode(scale_bytes).value
80
- result[:nonce] = Scale::Types.get("Compact").decode(scale_bytes).value
81
- result[:tip] = Scale::Types.get("Compact").decode(scale_bytes).value
63
+ result[:address] = Scale::Types.get("Address").decode(scale_bytes).value
64
+ result[:signature] = scale::types.get("MultiSignature").decode(scale_bytes).value
65
+ result[:era] = scale::types.get("era").decode(scale_bytes).value
66
+ result[:nonce] = scale::types.get("compact").decode(scale_bytes).value
67
+ result[:tip] = scale::types.get("compact").decode(scale_bytes).value
82
68
  result[:extrinsic_hash] = generate_hash(scale_bytes.bytes)
83
69
  end
84
70
  result[:call_index] = scale_bytes.get_next_bytes(2).bytes_to_hex[2..]
@@ -146,7 +132,7 @@ module Scale
146
132
  include SingleValue
147
133
 
148
134
  def self.decode(scale_bytes)
149
- metadata = Scale::TypeRegistry.instance.metadata
135
+ metadata = Scale::TypeRegistry.instance.metadata.value
150
136
 
151
137
  result = {}
152
138
  phase = scale_bytes.get_next_bytes(1).first
@@ -175,18 +161,18 @@ module Scale
175
161
  end
176
162
  end
177
163
 
178
- # log
179
- class Other < Bytes; end
180
-
181
164
  class AuthoritiesChange
182
165
  include Vec
183
166
  inner_type "AccountId"
184
167
  end
185
168
 
186
- class ConsensusEngineId < VecU8Length4; end
169
+ class GenericConsensusEngineId < VecU8Length4; end
187
170
 
188
171
  class ChangesTrieRoot < Bytes; end
189
172
 
173
+ # log
174
+ class Other < Bytes; end
175
+
190
176
  class SealV0
191
177
  include Struct
192
178
  items(