scale.rb 0.2.9 → 0.2.15

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