scale.rb 0.2.10 → 0.2.16

Sign up to get free protection for your applications and to get access to all the features.
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(