scale.rb 0.2.9 → 0.2.15

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/lib/scale/trie.rb ADDED
@@ -0,0 +1,171 @@
1
+ module Scale
2
+ module Types
3
+
4
+ class TrieNode
5
+ include SingleValue
6
+ EMPTY = 0
7
+ NIBBLE_PER_BYTE = 2
8
+ BITMAP_LENGTH = 2
9
+ NIBBLE_LENGTH = 16
10
+ NIBBLE_SIZE_BOUND = 65535
11
+
12
+ def self.decode(scale_bytes)
13
+ hash = "0x#{Crypto::blake2_256(scale_bytes.bytes)}"
14
+ first = scale_bytes.get_next_bytes(1).first
15
+ if first == EMPTY
16
+ TrieNode.new({})
17
+ else
18
+ v = first & (0b11 << 6)
19
+ decode_size = -> {
20
+ result = first & 255 >> 2
21
+ return result if result < 63
22
+ result -= 1
23
+ while result <= NIBBLE_SIZE_BOUND
24
+ n = scale_bytes.get_next_bytes(1).first
25
+ return (result + n + 1) if n < 255
26
+ result += 255
27
+ end
28
+ return NIBBLE_SIZE_BOUND
29
+ }
30
+
31
+ if v == 0b01 << 6 # leaf
32
+ nibble_count = decode_size.call
33
+ # if nibble_count is odd, the half of first byte of partial is 0
34
+ padding = (nibble_count % NIBBLE_PER_BYTE) != 0
35
+ first_byte_of_partial = scale_bytes.bytes[scale_bytes.offset]
36
+ if padding && (first_byte_of_partial & 0xf0) != 0
37
+ raise "bad format"
38
+ end
39
+
40
+ ### partial decoding
41
+ partial_bytes = scale_bytes.get_next_bytes((nibble_count + (NIBBLE_PER_BYTE - 1)) / NIBBLE_PER_BYTE)
42
+
43
+ ### value
44
+ count = Compact.decode(scale_bytes).value
45
+ value_bytes = scale_bytes.get_next_bytes(count)
46
+
47
+ return TrieNode.new({
48
+ hash: hash,
49
+ node_type: "leaf",
50
+ partial: {
51
+ hex: partial_bytes.bytes_to_hex,
52
+ padding: padding
53
+ },
54
+ value: value_bytes.bytes_to_hex
55
+ })
56
+ elsif v == 0b10 << 6 || v == 0b11 << 6 # branch without mask || branch with mask
57
+ nibble_count = decode_size.call
58
+
59
+ ### check that the padding is valid (if any)
60
+ # if nibble_count is odd, the half of first byte of partial is 0
61
+ padding = nibble_count % NIBBLE_PER_BYTE != 0
62
+ first_byte_of_partial = scale_bytes.bytes[scale_bytes.offset]
63
+ if padding && (first_byte_of_partial & 0xf0) != 0
64
+ raise "bad format"
65
+ end
66
+
67
+ ### partial decoding
68
+ partial_bytes = scale_bytes.get_next_bytes((nibble_count + (NIBBLE_PER_BYTE - 1)) / NIBBLE_PER_BYTE)
69
+
70
+ ### value decoding
71
+ if v == 0b11 << 6 # has value
72
+ count = Compact.decode(scale_bytes).value
73
+ value_bytes = scale_bytes.get_next_bytes(count)
74
+ end
75
+
76
+ ### children decoding
77
+ children = []
78
+ bitmap = U16.decode(scale_bytes).value
79
+ NIBBLE_LENGTH.times do |i|
80
+ has_child = (bitmap & (1 << i)) != 0
81
+ children[i] = nil
82
+ if has_child
83
+ count = Compact.decode(scale_bytes).value
84
+ if count == 32
85
+ h = H256.decode(scale_bytes).value
86
+ children[i] = h
87
+ else
88
+ inline = scale_bytes.get_next_bytes count
89
+ children[i] = inline.bytes_to_hex
90
+ end
91
+ end
92
+ end
93
+ # debug
94
+ # children.each_with_index do |child, i|
95
+ # if child.nil?
96
+ # puts "#{i}: NULL"
97
+ # else
98
+ # puts "#{i}: #{child}"
99
+ # end
100
+ # end
101
+
102
+ result = TrieNode.new({
103
+ hash: hash,
104
+ node_type: "branch",
105
+ partial: {
106
+ hex: partial_bytes.bytes_to_hex,
107
+ padding: padding
108
+ },
109
+ children: children
110
+ })
111
+
112
+ result[:value] = value_bytes.bytes_to_hex if value_bytes
113
+
114
+ return result
115
+ else
116
+ puts "Not support"
117
+ end
118
+
119
+ end
120
+ end
121
+
122
+ def self.check(root, proof, key)
123
+ key = Key.new(key)
124
+
125
+ nodes = proof.map {|node_data|
126
+ node = TrieNode::decode(Scale::Bytes.new(node_data)).value
127
+ [node[:hash], node]
128
+ }.to_h
129
+
130
+ self.do_check(root, nodes, key)
131
+ end
132
+
133
+ private
134
+ def self.do_check(hash, nodes, key)
135
+ if node = nodes[hash]
136
+ if node[:children]
137
+ position = key.next_nibble(node[:partial][:hex], node[:partial][:padding]).to_i(16)
138
+ child = node[:children][position]
139
+ return self.do_check(child, nodes, key)
140
+ else
141
+ return node[:value]
142
+ end
143
+ else
144
+ raise "Fail"
145
+ end
146
+ end
147
+ end
148
+
149
+ end
150
+ end
151
+
152
+ class Key
153
+ def initialize(value)
154
+ @value = value[2..] if value.start_with?("0x")
155
+ @offset = 0
156
+ end
157
+
158
+ def next_nibble(partial, padding)
159
+ partial = partial[2..] if partial.start_with?("0x")
160
+ partial = partial[1..] if padding
161
+
162
+ new_offset = @offset + partial.length
163
+ if partial == @value[@offset...new_offset]
164
+ nibble = @value[new_offset]
165
+ @offset = new_offset + 1
166
+ return nibble
167
+ else
168
+ raise "Fail"
169
+ end
170
+ end
171
+ end
data/lib/scale/types.rb CHANGED
@@ -225,59 +225,71 @@ module Scale
225
225
  end
226
226
  end
227
227
 
228
- class Address
228
+ class GenericAddress
229
229
  include SingleValue
230
- attr_accessor :account_length, :account_index, :account_id, :account_idx
231
230
 
231
+ # https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)
232
+ # base58encode ( concat ( <address-type>, <address>, <checksum> ) )
233
+ # ^^^^^^^^^
234
+ # the <address> is 32 byte account id or 1, 2, 4, 8 byte account index
235
+ # scale_bytes: account length byte + <address>'s bytes
232
236
  def self.decode(scale_bytes)
233
237
  account_length = scale_bytes.get_next_bytes(1).first
234
238
 
235
- if account_length == 0xff # 255
239
+ if account_length == 0xff # 32 bytes address(Public key)
236
240
  account_id = scale_bytes.get_next_bytes(32).bytes_to_hex
237
- account_length = account_length.to_s(16)
238
- Address.new(account_id)
241
+ account_length = [account_length].bytes_to_hex
242
+
243
+ Address.new({
244
+ account_id: account_id,
245
+ account_length: account_length
246
+ })
239
247
  else
240
248
  account_index =
241
- if account_length == 0xfc
249
+ if account_length == 0xfc # 2 bytes address(account index)
242
250
  scale_bytes.get_next_bytes(2).bytes_to_hex
243
- elsif account_length == 0xfd
251
+ elsif account_length == 0xfd # 4 bytes address(account index)
244
252
  scale_bytes.get_next_bytes(4).bytes_to_hex
245
- elsif account_length == 0xfe
253
+ elsif account_length == 0xfe # 8 bytes address(account index)
246
254
  scale_bytes.get_next_bytes(8).bytes_to_hex
247
255
  else
248
256
  [account_length].bytes_to_hex
249
257
  end
250
- # account_idx =
251
- account_length = account_length.to_s(16)
252
- Address.new(account_index)
258
+ # TODO: add account_idx
259
+ account_length = [account_length].bytes_to_hex
260
+
261
+ Address.new({
262
+ account_index: account_index,
263
+ account_length: account_length
264
+ })
253
265
  end
254
266
  end
255
267
 
256
- def encode(ss58=false, addr_type=42)
257
- if value.start_with?("0x")
258
- if ss58 === true
259
- ::Address.encode(value, addr_type)
260
- else
261
- prefix = if value.length == 66
262
- "ff"
263
- elsif value.length == 6
264
- "fc"
265
- elsif value.length == 10
266
- "fd"
267
- elsif value.length == 18
268
- "fe"
269
- else
270
- ""
271
- end
272
- "#{prefix}#{value[2..]}"
273
- end
268
+ def encode
269
+ if self.value[:account_id]
270
+ "#{self.value[:account_length][2..]}#{self.value[:account_id][2..]}"
274
271
  else
275
- raise "Format error"
272
+ "#{self.value[:account_length][2..]}#{self.value[:account_index][2..]}"
276
273
  end
277
274
  end
278
275
  end
279
276
 
280
- class RawAddress < Address; end
277
+ class Address < GenericAddress; end
278
+
279
+ class RawAddress < GenericAddress; end
280
+
281
+ class AccountIdAddress < GenericAddress
282
+ def self.decode(scale_bytes)
283
+ AccountIdAddress.new({
284
+ account_id: AccountId.decode(scale_bytes).value,
285
+ account_length: "0xff"
286
+ })
287
+ end
288
+
289
+ def encode
290
+ "ff#{self.value[:account_id][2..]}"
291
+ end
292
+ end
281
293
 
282
294
  class AccountId < H256; end
283
295
 
@@ -558,34 +570,52 @@ module Scale
558
570
 
559
571
  class VecU8Length2
560
572
  include VecU8FixedLength
573
+ BYTE_LENGTH = 2
561
574
  end
562
575
 
563
576
  class VecU8Length3
564
577
  include VecU8FixedLength
578
+ BYTE_LENGTH = 3
565
579
  end
566
580
 
567
581
  class VecU8Length4
568
582
  include VecU8FixedLength
583
+ BYTE_LENGTH = 4
569
584
  end
570
585
 
571
586
  class VecU8Length8
572
587
  include VecU8FixedLength
588
+ BYTE_LENGTH = 8
573
589
  end
574
590
 
575
591
  class VecU8Length16
576
592
  include VecU8FixedLength
593
+ BYTE_LENGTH = 16
577
594
  end
578
595
 
579
596
  class VecU8Length20
580
597
  include VecU8FixedLength
598
+ BYTE_LENGTH = 20
581
599
  end
582
600
 
583
601
  class VecU8Length32
584
602
  include VecU8FixedLength
603
+ BYTE_LENGTH = 32
585
604
  end
586
605
 
587
606
  class VecU8Length64
588
607
  include VecU8FixedLength
608
+ BYTE_LENGTH = 64
609
+ end
610
+
611
+ class VecU8Length128
612
+ include VecU8FixedLength
613
+ BYTE_LENGTH = 128
614
+ end
615
+
616
+ class VecU8Length256
617
+ include VecU8FixedLength
618
+ BYTE_LENGTH = 256
589
619
  end
590
620
 
591
621
  class BalanceLock
data/lib/scale/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Scale
2
- VERSION = "0.2.9".freeze
2
+ VERSION = "0.2.15".freeze
3
3
  end
@@ -0,0 +1,159 @@
1
+
2
+ def ws_request(url, payload)
3
+ result = nil
4
+ Kontena::Websocket::Client.connect(url, {}) do |client|
5
+ client.send(payload.to_json)
6
+
7
+ client.read do |message|
8
+ result = JSON.parse message
9
+ client.close(1000)
10
+ end
11
+ end
12
+
13
+ return result
14
+ rescue Kontena::Websocket::CloseError => e
15
+ raise SubstrateClient::WebsocketError, e.reason
16
+ rescue Kontena::Websocket::Error => e
17
+ raise SubstrateClient::WebsocketError, e.reason
18
+ end
19
+
20
+ class SubstrateClient
21
+ class WebsocketError < StandardError; end
22
+ class RpcError < StandardError; end
23
+ class RpcTimeout < StandardError; end
24
+
25
+ attr_reader :metadata
26
+ attr_reader :spec_name, :spec_version
27
+
28
+ def initialize(url)
29
+ @url = url
30
+ @request_id = 1
31
+ @metadata_cache = {}
32
+ end
33
+
34
+ def request(method, params)
35
+ payload = {
36
+ "jsonrpc" => "2.0",
37
+ "method" => method,
38
+ "params" => params,
39
+ "id" => @request_id
40
+ }
41
+
42
+ data = ws_request(@url, payload)
43
+ if data["error"]
44
+ raise RpcError, data["error"]
45
+ else
46
+ data["result"]
47
+ end
48
+ end
49
+
50
+ def init_types_and_metadata(block_hash=nil)
51
+ runtime_version = self.state_getRuntimeVersion(block_hash)
52
+ spec_name = runtime_version["specName"].downcase
53
+ spec_version = runtime_version["specVersion"]
54
+
55
+ registry = Scale::TypeRegistry.instance
56
+
57
+ # load types
58
+ if registry.types == nil
59
+ registry.load(spec_name: spec_name)
60
+ end
61
+ registry.spec_version = spec_version
62
+
63
+ # set current metadata
64
+ metadata = @metadata_cache[spec_version]
65
+ if metadata.nil?
66
+ hex = self.state_getMetadata(block_hash)
67
+ metadata = Scale::Types::Metadata.decode(Scale::Bytes.new(hex))
68
+ @metadata_cache[spec_version] = metadata
69
+ end
70
+
71
+ @metadata = metadata
72
+ registry.metadata = metadata
73
+
74
+ true
75
+ end
76
+
77
+ def get_metadata_from_cache(spec_version)
78
+
79
+ end
80
+
81
+ def invoke(method, *params)
82
+ request(method, params)
83
+ end
84
+
85
+ # ################################################
86
+ # origin rpc methods
87
+ # ################################################
88
+ def method_missing(method, *args)
89
+ invoke method, *args
90
+ end
91
+
92
+ # ################################################
93
+ # custom methods based on origin rpc methods
94
+ # ################################################
95
+ def methods
96
+ invoke("rpc_methods")["methods"]
97
+ end
98
+
99
+ def get_block_number(block_hash)
100
+ header = self.chain_getHeader(block_hash)
101
+ header["number"].to_i(16)
102
+ end
103
+
104
+ def get_metadata(block_hash=nil)
105
+ self.init_types_and_metadata(block_hash)
106
+ @metadata
107
+ end
108
+
109
+ def get_block(block_hash=nil)
110
+ self.init_types_and_metadata(block_hash)
111
+ block = self.chain_getBlock(block_hash)
112
+ SubstrateClient::Helper.decode_block(block)
113
+ rescue => ex
114
+ puts ex.message
115
+ puts ex.backtrace.join("\n\t")
116
+ end
117
+
118
+ def get_block_events(block_hash=nil)
119
+ self.init_types_and_metadata(block_hash)
120
+
121
+ storage_key = "0x26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"
122
+ events_data = state_getStorage storage_key, block_hash
123
+
124
+ scale_bytes = Scale::Bytes.new(events_data)
125
+ Scale::Types.get("Vec<EventRecord>").decode(scale_bytes).to_human
126
+ end
127
+
128
+ # Plain: client.get_storage("Sudo", "Key")
129
+ # Plain: client.get_storage("Balances", "TotalIssuance")
130
+ # Map: client.get_storage("System", "Account", ["0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"])
131
+ # DoubleMap: client.get_storage("ImOnline", "AuthoredBlocks", [2818, "0x749ddc93a65dfec3af27cc7478212cb7d4b0c0357fef35a0163966ab5333b757"])
132
+ def get_storage(module_name, storage_name, params = nil, block_hash = nil)
133
+ self.init_types_and_metadata(block_hash)
134
+
135
+ storage_key, return_type = SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
136
+ data = self.state_getStorage(storage_key, block_hash)
137
+ return unless data
138
+
139
+ bytes = Scale::Bytes.new(data)
140
+ type = Scale::Types.get(return_type)
141
+ type.decode(bytes)
142
+ end
143
+
144
+ def generate_storage_key(module_name, storage_name, params = nil, block_hash = nil)
145
+ self.init_types_and_metadata(block_hash)
146
+ SubstrateClient::Helper.generate_storage_key_from_metadata(@metadata, module_name, storage_name, params)
147
+ end
148
+
149
+ # compose_call "Balances", "Transfer", { dest: "0x586cb27c291c813ce74e86a60dad270609abf2fc8bee107e44a80ac00225c409", value: 1_000_000_000_000 }
150
+ def compose_call(module_name, call_name, params, block_hash=nil)
151
+ self.init_types_and_metadata(block_hash)
152
+ SubstrateClient::Helper.compose_call_from_metadata(@metadata, module_name, call_name, params)
153
+ end
154
+
155
+ def generate_storage_hash_from_data(storage_hex_data)
156
+ "0x" + Crypto.blake2_256(Scale::Bytes.new(storage_hex_data).bytes)
157
+ end
158
+
159
+ end