scale.rb 0.2.12 → 0.2.17

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,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- scale.rb (0.2.12)
5
- activesupport (>= 4.0.0)
4
+ scale.rb (0.2.17)
5
+ base58
6
+ blake2b_rs (~> 0.1.2)
7
+ faye-websocket
6
8
  json (~> 2.3.0)
7
- substrate_common.rb (~> 0.1.9)
8
- thor (~> 0.19.0)
9
+ xxhash
9
10
 
10
11
  GEM
11
12
  remote: https://rubygems.org/
12
13
  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
14
  base58 (0.2.3)
20
- blake2b (0.10.0)
15
+ blake2b_rs (0.1.2)
16
+ ffi (= 1.15.0)
17
+ thermite (~> 0)
21
18
  coderay (1.1.2)
22
- concurrent-ruby (1.1.7)
23
19
  diff-lcs (1.3)
24
- ffi (1.12.2)
25
- i18n (1.8.5)
26
- concurrent-ruby (~> 1.0)
20
+ eventmachine (1.2.7)
21
+ faye-websocket (0.11.0)
22
+ eventmachine (>= 0.12.0)
23
+ websocket-driver (>= 0.5.1)
24
+ ffi (1.15.0)
27
25
  json (2.3.1)
28
26
  method_source (0.9.2)
29
- minitest (5.14.2)
27
+ minitar (0.9)
30
28
  pry (0.12.2)
31
29
  coderay (~> 1.1.0)
32
30
  method_source (~> 0.9.0)
@@ -44,27 +42,26 @@ GEM
44
42
  diff-lcs (>= 1.2.0, < 2.0)
45
43
  rspec-support (~> 3.9.0)
46
44
  rspec-support (3.9.0)
47
- substrate_common.rb (0.1.9)
48
- base58
49
- blake2b
50
- xxhash
51
- thor (0.19.4)
52
- thread_safe (0.3.6)
53
- tzinfo (1.2.7)
54
- thread_safe (~> 0.1)
45
+ thermite (0.13.0)
46
+ minitar (~> 0.5)
47
+ rake (>= 10)
48
+ tomlrb (~> 1.2)
49
+ tomlrb (1.3.0)
50
+ websocket-driver (0.7.3)
51
+ websocket-extensions (>= 0.1.0)
52
+ websocket-extensions (0.1.5)
55
53
  xxhash (0.4.0)
56
- zeitwerk (2.4.1)
57
54
 
58
55
  PLATFORMS
59
56
  ruby
60
57
 
61
58
  DEPENDENCIES
62
- bundler (~> 1.17)
63
- ffi (~> 1.12)
59
+ bundler
60
+ ffi (~> 1.15.0)
64
61
  pry
65
62
  rake (~> 13.0)
66
63
  rspec (~> 3.2)
67
64
  scale.rb!
68
65
 
69
66
  BUNDLED WITH
70
- 1.17.3
67
+ 2.2.13
data/README.md CHANGED
@@ -19,7 +19,7 @@ Because the feature of ruby 2.6 is used, the ruby version is required to be >= 2
19
19
  Add this line to your application's Gemfile:
20
20
 
21
21
  ```ruby
22
- gem 'scale.rb'
22
+ gem 'scale.rb', '0.2.16'
23
23
  ```
24
24
 
25
25
  And then execute:
data/lib/address.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'blake2b'
2
+ require 'base58'
3
+
data/lib/common.rb ADDED
@@ -0,0 +1,163 @@
1
+ require 'xxhash'
2
+ require 'blake2b'
3
+ require 'base58'
4
+
5
+ class Array
6
+ def bytes_to_hex
7
+ raise "Not a byte array" unless self.is_byte_array?
8
+ '0x' + self.map { |b| b.to_s(16).rjust(2, '0') }.join
9
+ end
10
+
11
+ def bytes_to_bin
12
+ raise "Not a byte array" unless self.is_byte_array?
13
+ '0b' + self.map { |b| b.to_s(2).rjust(8, '0') }.join
14
+ end
15
+
16
+ def bytes_to_bin
17
+ raise "Not a byte array" unless self.is_byte_array?
18
+ self.map { |b| b.to_s(2).rjust(8, '0') }
19
+ end
20
+
21
+ def bytes_to_utf8
22
+ raise "Not a byte array" unless self.is_byte_array?
23
+ self.pack('C*').force_encoding('utf-8')
24
+ end
25
+
26
+ def is_byte_array?
27
+ self.all? {|e| e >= 0 and e <= 255 }
28
+ end
29
+ end
30
+
31
+ class String
32
+ def constantize2
33
+ Object.const_get(self)
34
+ end
35
+
36
+ def hex_to_bytes
37
+ data = self.start_with?('0x') ? self[2..] : self
38
+ raise "Not valid hex string" if data.length % 2 != 0
39
+ data.scan(/../).map(&:hex)
40
+ end
41
+ end
42
+
43
+ module Crypto
44
+ def self.identity(bytes)
45
+ bytes.bytes_to_hex[2..]
46
+ end
47
+
48
+ def self.twox64(data)
49
+ result = XXhash.xxh64 data, 0
50
+ bytes = result.to_s(16).rjust(16, '0').hex_to_bytes.reverse
51
+ bytes.bytes_to_hex[2..]
52
+ end
53
+
54
+ def self.twox128(data)
55
+ bytes = []
56
+ 2.times do |i|
57
+ result = XXhash.xxh64 data, i
58
+ bytes = bytes + result.to_s(16).rjust(16, '0').hex_to_bytes.reverse
59
+ end
60
+ bytes.bytes_to_hex[2..]
61
+ end
62
+
63
+ def self.twox64_concat(bytes)
64
+ data = bytes.bytes_to_utf8
65
+ twox64(data) + bytes.bytes_to_hex[2..]
66
+ end
67
+
68
+ def self.blake2_128(bytes)
69
+ Blake2b.hex bytes, 16
70
+ end
71
+
72
+ def self.blake2_256(bytes)
73
+ Blake2b.hex bytes, 32
74
+ end
75
+
76
+ def self.blake2_128_concat(bytes)
77
+ blake2_128(bytes) + bytes.bytes_to_hex[2..]
78
+ end
79
+ end
80
+
81
+ class Address
82
+ SS58_PREFIX = 'SS58PRE'
83
+
84
+ TYPES = [
85
+ # Polkadot Live (SS58, AccountId)
86
+ 0, 1,
87
+ # Polkadot Canary (SS58, AccountId)
88
+ 2, 3,
89
+ # Kulupu (SS58, Reserved)
90
+ 16, 17,
91
+ # Darwinia Live
92
+ 18,
93
+ # Dothereum (SS58, AccountId)
94
+ 20, 21,
95
+ # Generic Substrate wildcard (SS58, AccountId)
96
+ 42, 43,
97
+
98
+ # Schnorr/Ristretto 25519 ("S/R 25519") key
99
+ 48,
100
+ # Edwards Ed25519 key
101
+ 49,
102
+ # ECDSA SECP256k1 key
103
+ 50,
104
+
105
+ # Reserved for future address format extensions.
106
+ *64..255
107
+ ]
108
+
109
+ class << self
110
+
111
+ def array_to_hex_string(arr)
112
+ body = arr.map { |i| i.to_s(16).rjust(2, '0') }.join
113
+ "0x#{body}"
114
+ end
115
+
116
+ def decode(address, addr_type = 42, ignore_checksum = true)
117
+ decoded = Base58.base58_to_binary(address, :bitcoin)
118
+ is_pubkey = decoded.size == 35
119
+
120
+ size = decoded.size - ( is_pubkey ? 2 : 1 )
121
+
122
+ prefix = decoded[0, 1].unpack("C*").first
123
+
124
+ raise "Invalid address type" unless TYPES.include?(addr_type)
125
+
126
+ hash_bytes = make_hash(decoded[0, size])
127
+ if is_pubkey
128
+ is_valid_checksum = decoded[-2].unpack("C*").first == hash_bytes[0] && decoded[-1].unpack("C*").first == hash_bytes[1]
129
+ else
130
+ is_valid_checksum = decoded[-1].unpack("C*").first == hash_bytes[0]
131
+ end
132
+
133
+ raise "Invalid decoded address checksum" unless is_valid_checksum && ignore_checksum
134
+
135
+ decoded[1...size].unpack("H*").first
136
+ end
137
+
138
+
139
+ def encode(pubkey, addr_type = 42)
140
+ pubkey = pubkey[2..-1] if pubkey =~ /^0x/i
141
+ key = [pubkey].pack("H*")
142
+
143
+ u8_array = key.bytes
144
+
145
+ u8_array.unshift(addr_type)
146
+
147
+ bytes = make_hash(u8_array.pack("C*"))
148
+
149
+ checksum = bytes[0, key.size == 32 ? 2 : 1]
150
+
151
+ u8_array.push(*checksum)
152
+
153
+ input = u8_array.pack("C*")
154
+
155
+ Base58.binary_to_base58(input, :bitcoin)
156
+ end
157
+
158
+ def make_hash(body)
159
+ Blake2b.bytes("#{SS58_PREFIX}#{body}", Blake2b::Key.none, 64)
160
+ end
161
+
162
+ end
163
+ end
data/lib/helper.rb ADDED
@@ -0,0 +1,128 @@
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.underscore2, 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.underscore2, 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_decoded"] = []
112
+ block["block"]["extrinsics"].each_with_index do |hex, i|
113
+ scale_bytes = Scale::Bytes.new(hex)
114
+ block["block"]["extrinsics_decoded"][i] = Scale::Types::Extrinsic.decode(scale_bytes).to_human
115
+ end
116
+
117
+ block['block']['header']["digest"]["logs_decoded"] = []
118
+ block['block']['header']["digest"]["logs"].each_with_index do |hex, i|
119
+ scale_bytes = Scale::Bytes.new(hex)
120
+ log = Scale::Types::LogDigest.decode(scale_bytes).to_human
121
+ block['block']['header']["digest"]["logs_decoded"][i] = log
122
+ end
123
+
124
+ block
125
+ end
126
+
127
+ end
128
+ end
@@ -7,7 +7,7 @@ module Scale
7
7
  bytes = scale_bytes.get_next_bytes(4)
8
8
  if bytes.bytes_to_utf8 == "meta"
9
9
  metadata_version = Scale::Types.type_of("Enum", %w[MetadataV0 MetadataV1 MetadataV2 MetadataV3 MetadataV4 MetadataV5 MetadataV6 MetadataV7 MetadataV8 MetadataV9 MetadataV10 MetadataV11 MetadataV12]).decode(scale_bytes).value
10
- metadata = Metadata.new "Scale::Types::#{metadata_version}".constantize.decode(scale_bytes)
10
+ metadata = Metadata.new "Scale::Types::#{metadata_version}".constantize2.decode(scale_bytes)
11
11
  metadata.version = metadata_version[9..].to_i
12
12
  else
13
13
  scale_bytes.reset_offset
@@ -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