scale.rb 0.2.16 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Cargo.lock +8 -4
  4. data/Cargo.toml +2 -3
  5. data/Dockerfile +4 -1
  6. data/Gemfile.lock +43 -35
  7. data/README.md +44 -1
  8. data/Rakefile +6 -0
  9. data/exe/scale +39 -79
  10. data/lib/address.rb +3 -0
  11. data/lib/common.rb +163 -0
  12. data/lib/helper.rb +25 -8
  13. data/lib/metadata/metadata.rb +28 -18
  14. data/lib/metadata/metadata_v0.rb +24 -20
  15. data/lib/metadata/metadata_v1.rb +13 -9
  16. data/lib/metadata/metadata_v10.rb +2 -2
  17. data/lib/metadata/metadata_v11.rb +2 -2
  18. data/lib/metadata/metadata_v12.rb +9 -8
  19. data/lib/metadata/metadata_v13.rb +161 -0
  20. data/lib/metadata/metadata_v2.rb +2 -2
  21. data/lib/metadata/metadata_v3.rb +2 -2
  22. data/lib/metadata/metadata_v4.rb +21 -11
  23. data/lib/metadata/metadata_v5.rb +21 -11
  24. data/lib/metadata/metadata_v6.rb +9 -9
  25. data/lib/metadata/metadata_v7.rb +26 -15
  26. data/lib/metadata/metadata_v8.rb +9 -9
  27. data/lib/metadata/metadata_v9.rb +2 -2
  28. data/lib/scale.rb +41 -341
  29. data/lib/scale/base.rb +177 -95
  30. data/lib/scale/block.rb +17 -13
  31. data/lib/scale/trie.rb +1 -1
  32. data/lib/scale/types.rb +139 -40
  33. data/lib/scale/version.rb +1 -1
  34. data/lib/scale_bytes.rb +63 -0
  35. data/lib/substrate_client.rb +31 -17
  36. data/lib/type_builder.rb +279 -0
  37. data/lib/type_registry.rb +91 -0
  38. data/lib/type_registry/crab.json +676 -595
  39. data/lib/type_registry/darwinia.json +730 -554
  40. data/lib/type_registry/default.json +3 -2
  41. data/lib/type_registry/pangolin.json +771 -0
  42. data/scale.gemspec +7 -5
  43. data/scripts/mmr_root_to_sign.rb +10 -0
  44. data/src/lib.rs +80 -25
  45. metadata +59 -30
  46. data/lib/type_registry/edgeware.json +0 -124
  47. data/lib/type_registry/joystream.json +0 -49
  48. data/lib/type_registry/kulupu.json +0 -15
  49. data/lib/type_registry/plasm.json +0 -89
  50. data/lib/type_registry/robonomics.json +0 -39
  51. data/lib/type_registry/westend.json +0 -63
  52. data/src/storage_key.rs +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '06486e89da3732a84ca4f980e8b01e135521e51b4a26b0d67d5684e9abc5ad85'
4
- data.tar.gz: b23b2ba8bd7ca6b9746caa8019f73710a6c2cd4613576fb6d0ac4da433aeb0ea
3
+ metadata.gz: 2ce5a456e8313b36d61cbfbff272c86985a6dc240603b37d6e96bd1cc0f28a9b
4
+ data.tar.gz: fda311dc30d0f8b31c62d9332bf241bab5b5646b6919fcb3b41814126692f469
5
5
  SHA512:
6
- metadata.gz: 9407464fea76e85c960219fd19eea5db480f207db7b6f24a96ddfb819c06ce822c1b3649c1a3064e2be697bb3305b0328e0dbb9d04fc8f69851673b3b6a54bbe
7
- data.tar.gz: 6c54d51a1af71fc24504eb77a1182248923386629ebc1e0a86002d8a3b299ef1deea19c388561430c4e7cb4102b3e7669726d5ec6fa9479f6a04c82feeac7863
6
+ metadata.gz: 2d9d0c5ccc5cdc2e34515af88a64164678ce6f18a128f3e935906a362979ff6be7e83c0ea9ab17350acb3fb91e67766dbc3bc247fc733dd4310c9efb3aaf6857
7
+ data.tar.gz: 9c0a9f424308e368dc886ad31f568b118fa5596d21b7d5cd9d79bbefcf41a8324c4d3f885f24334165cac769e33da988f2b1f03097a966f19003b7c027cd8ab3
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  /.idea
11
11
  /target
12
12
  .DS_Store
13
+ /scripts/tmp.rb
data/Cargo.lock CHANGED
@@ -1,5 +1,7 @@
1
1
  # This file is automatically @generated by Cargo.
2
2
  # It is not intended for manual editing.
3
+ version = 3
4
+
3
5
  [[package]]
4
6
  name = "Inflector"
5
7
  version = "0.11.4"
@@ -15,6 +17,8 @@ name = "SCALE-testing-interface"
15
17
  version = "0.1.0"
16
18
  dependencies = [
17
19
  "frame-support",
20
+ "hex",
21
+ "libc",
18
22
  "parity-scale-codec",
19
23
  "sp-core",
20
24
  ]
@@ -642,9 +646,9 @@ dependencies = [
642
646
 
643
647
  [[package]]
644
648
  name = "hex"
645
- version = "0.4.2"
649
+ version = "0.4.3"
646
650
  source = "registry+https://github.com/rust-lang/crates.io-index"
647
- checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
651
+ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
648
652
 
649
653
  [[package]]
650
654
  name = "hmac"
@@ -744,9 +748,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
744
748
 
745
749
  [[package]]
746
750
  name = "libc"
747
- version = "0.2.82"
751
+ version = "0.2.94"
748
752
  source = "registry+https://github.com/rust-lang/crates.io-index"
749
- checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
753
+ checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
750
754
 
751
755
  [[package]]
752
756
  name = "libsecp256k1"
data/Cargo.toml CHANGED
@@ -8,11 +8,10 @@ edition = "2018"
8
8
  parity-scale-codec = { version = "1.3.6" }
9
9
  sp-core = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git", features = ["full_crypto"]}
10
10
  frame-support = { version = "2.0.0", git = "https://github.com/paritytech/substrate.git" }
11
+ libc = { version = "0.2.94" }
12
+ hex = { version = "0.4.3" }
11
13
 
12
14
  [lib]
13
15
  name = "scale_ffi"
14
16
  crate-type = ["dylib"]
15
17
 
16
- [[bin]]
17
- name = "storage_key"
18
- path = "src/storage_key.rs"
data/Dockerfile CHANGED
@@ -15,7 +15,10 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
15
15
  export RUSTFLAGS='-C target-feature=-crt-static' && \
16
16
  make
17
17
 
18
- RUN gem install bundler:1.17.3 && \
18
+ ENV RUSTFLAGS='-C target-feature=-crt-static'
19
+ ENV PATH=/root/.cargo/bin:$PATH
20
+
21
+ RUN gem install bundler:2.2.13 && \
19
22
  bundle install && \
20
23
  rake install:local
21
24
 
data/Gemfile.lock CHANGED
@@ -1,47 +1,55 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- scale.rb (0.2.16)
4
+ scale.rb (0.3.0)
5
+ base58
6
+ blake2b_rs (~> 0.1.2)
7
+ faye-websocket
5
8
  json (~> 2.3.0)
6
- kontena-websocket-client (~> 0.1.1)
7
- substrate_common.rb (~> 0.1.10)
8
- thor (~> 0.19.0)
9
+ thor (~> 1.0)
10
+ xxhash
9
11
 
10
12
  GEM
11
13
  remote: https://rubygems.org/
12
14
  specs:
13
15
  base58 (0.2.3)
14
- blake2b (0.10.0)
15
- coderay (1.1.2)
16
- diff-lcs (1.3)
17
- ffi (1.12.2)
16
+ blake2b_rs (0.1.2)
17
+ ffi (= 1.15.0)
18
+ thermite (~> 0)
19
+ coderay (1.1.3)
20
+ diff-lcs (1.4.4)
21
+ eventmachine (1.2.7)
22
+ faye-websocket (0.11.1)
23
+ eventmachine (>= 0.12.0)
24
+ websocket-driver (>= 0.5.1)
25
+ ffi (1.15.0)
18
26
  json (2.3.1)
19
- kontena-websocket-client (0.1.1)
20
- websocket-driver (~> 0.6.5)
21
- method_source (0.9.2)
22
- pry (0.12.2)
23
- coderay (~> 1.1.0)
24
- method_source (~> 0.9.0)
25
- rake (13.0.1)
26
- rspec (3.9.0)
27
- rspec-core (~> 3.9.0)
28
- rspec-expectations (~> 3.9.0)
29
- rspec-mocks (~> 3.9.0)
30
- rspec-core (3.9.0)
31
- rspec-support (~> 3.9.0)
32
- rspec-expectations (3.9.0)
27
+ method_source (1.0.0)
28
+ minitar (0.9)
29
+ pry (0.14.0)
30
+ coderay (~> 1.1)
31
+ method_source (~> 1.0)
32
+ rake (13.0.3)
33
+ rspec (3.10.0)
34
+ rspec-core (~> 3.10.0)
35
+ rspec-expectations (~> 3.10.0)
36
+ rspec-mocks (~> 3.10.0)
37
+ rspec-core (3.10.1)
38
+ rspec-support (~> 3.10.0)
39
+ rspec-expectations (3.10.1)
33
40
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.9.0)
35
- rspec-mocks (3.9.0)
41
+ rspec-support (~> 3.10.0)
42
+ rspec-mocks (3.10.2)
36
43
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.9.0)
38
- rspec-support (3.9.0)
39
- substrate_common.rb (0.1.10)
40
- base58
41
- blake2b
42
- xxhash
43
- thor (0.19.4)
44
- websocket-driver (0.6.5)
44
+ rspec-support (~> 3.10.0)
45
+ rspec-support (3.10.2)
46
+ thermite (0.13.0)
47
+ minitar (~> 0.5)
48
+ rake (>= 10)
49
+ tomlrb (~> 1.2)
50
+ thor (1.1.0)
51
+ tomlrb (1.3.0)
52
+ websocket-driver (0.7.5)
45
53
  websocket-extensions (>= 0.1.0)
46
54
  websocket-extensions (0.1.5)
47
55
  xxhash (0.4.0)
@@ -50,12 +58,12 @@ PLATFORMS
50
58
  ruby
51
59
 
52
60
  DEPENDENCIES
53
- bundler (~> 1.17)
54
- ffi (~> 1.12)
61
+ bundler
62
+ ffi (~> 1.15.0)
55
63
  pry
56
64
  rake (~> 13.0)
57
65
  rspec (~> 3.2)
58
66
  scale.rb!
59
67
 
60
68
  BUNDLED WITH
61
- 1.17.3
69
+ 2.2.13
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  ![grants_badge](./grants_badge.png)
2
2
 
3
+ **Warning: After v0.2.19, the develop branch will do a structure refactor and big optimization.**
4
+
3
5
  # scale.rb
4
6
 
5
7
  **Ruby SCALE Codec Library**
@@ -19,7 +21,7 @@ Because the feature of ruby 2.6 is used, the ruby version is required to be >= 2
19
21
  Add this line to your application's Gemfile:
20
22
 
21
23
  ```ruby
22
- gem 'scale.rb'
24
+ gem 'scale.rb', '0.2.19'
23
25
  ```
24
26
 
25
27
  And then execute:
@@ -37,10 +39,22 @@ Or install it yourself as:
37
39
  ```ruby
38
40
  require "scale"
39
41
 
42
+ Scale::TypeRegistry.instance.load # default
43
+ # Scale::TypeRegistry.instance.load spec_name: "pangolin"
44
+ # Scale::TypeRegistry.instance.load spec_name: "kusama"
45
+
46
+ # print hex changes if you set debug to true, default is false
47
+ Scale::Types.debug = true
48
+
40
49
  # decode a compact integer
41
50
  scale_bytes = Scale::Bytes.new("0x1501") # create scale_bytes object from scale encoded hex string
42
51
  o = Scale::Types::Compact.decode(scale_bytes) # use scale type to decode scale_bytes object
43
52
  p o.value # 69
53
+
54
+ #
55
+ type = Scale::Types::get("Vec<U8>")
56
+ o = type.decode(Scale::Bytes.new("0x080001"))
57
+ assert_eq o.value, [Scale::Types::U8.new(0), Scale::Types::U8.new(1)]
44
58
  ```
45
59
 
46
60
  2. encode
@@ -48,8 +62,37 @@ p o.value # 69
48
62
  ```ruby
49
63
  require "scale"
50
64
 
65
+ Scale::TypeRegistry.instance.load
66
+
51
67
  o = Scale::Types::Compact.new(69)
52
68
  p o.encode # "1501"
69
+
70
+ type = Scale::Types::get("Vec<U8>")
71
+ o = type.new([Scale::Types::U8.new(0), Scale::Types::U8.new(1)])
72
+ p o.encode # "080001"
73
+ ```
74
+
75
+ 3. client
76
+ ```ruby
77
+ require "scale"
78
+ client = SubstrateClient.new "wss://rpc.darwinia.network"
79
+
80
+ v = Scale::Types.get("EthereumTransactionIndex")
81
+ .new(
82
+ [
83
+ Scale::Types::H256.new("0x803054c2beacabc36e15c3147bb87d8320a02e9b601be28820a622dedd1c7717"),
84
+ Scale::Types::U64.new(266)
85
+ ]
86
+ )
87
+
88
+ storage = client.get_storage("EthereumBacking", "VerifiedProof", [v])
89
+ puts storage.to_human
90
+
91
+ # get the raw data
92
+ key = client.generate_storage_key("EthereumBacking", "VerifiedProof", [v])[0]
93
+ storage_raw = client.state_getStorageAt(key)
94
+ puts storage_raw
95
+
53
96
  ```
54
97
  Please go to `spec` dir for more examples.
55
98
 
data/Rakefile CHANGED
@@ -1,2 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
+ require "scale"
2
3
  task default: :spec
4
+
5
+ desc 'Check types of a spec'
6
+ task :check_types do
7
+ TypeRegistry.instance.load spec_name: 'darwinia'
8
+ end
data/exe/scale CHANGED
@@ -1,88 +1,48 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'scale'
4
- require 'thor'
3
+ require "thor"
4
+ require "scale"
5
5
 
6
6
  class ScaleCli < Thor
7
- desc "specs", "list all chain specs"
8
- def specs
9
- path = File.join File.expand_path('../..', __FILE__), "lib", "type_registry", "*.json"
10
- specs = Dir[path].map do |file|
11
- File.basename file, ".json"
7
+ desc "list-types WS_ENDPOINT ", "list all types in metadata"
8
+ def list_types(ws)
9
+ client = SubstrateClient.new ws
10
+ metadata = client.get_metadata.value.to_human.to_json
11
+ metadata = JSON.parse(metadata)
12
+
13
+ types = []
14
+ metadata["metadata"]["modules"].each do |m|
15
+ if m["storage"]
16
+ m["storage"]["items"].each do |storage|
17
+ type = storage["type"]
18
+ if type["Plain"]
19
+ types << type["Plain"].gsub("\n ", "").gsub("\n", "")
20
+ elsif type["Map"]
21
+ types << type["Map"]["key"].gsub("\n", "")
22
+ types << type["Map"]["value"].gsub("\n", "")
23
+ end
24
+ end
25
+ end
26
+
27
+ if m["calls"]
28
+ m["calls"].each do |call|
29
+ call["args"].each do |arg|
30
+ types << arg["type"].gsub("\n", "")
31
+ end
32
+ end
33
+ end
34
+
35
+ if m["events"]
36
+ m["events"].each do |event|
37
+ event["args"].each do |arg|
38
+ types << arg.gsub("\n", "")
39
+ end
40
+ end
41
+ end
12
42
  end
13
- puts specs.join(", ")
14
- end
15
-
16
- # exmaples:
17
- #
18
- # scale types darwinia
19
- # =>
20
- # ProposalCategory => Scale::Types::ProposalCategory
21
- # VoteStage => Scale::Types::VoteStage
22
- # TallyType => Scale::Types::TallyType
23
- # ...
24
- #
25
- # Count: 279
26
- desc "types SPEC_NAME SPEC_VERSION", "list all types implemented for chain"
27
- def types(spec_name = "default", spec_version = nil)
28
- Scale::TypeRegistry.instance.load(spec_name, spec_version)
29
- Scale::Types.list.each_pair do |type_name, type|
30
- puts "#{green(type_name)} => #{yellow(type.to_s)}"
31
- end
32
-
33
- puts "\nCount: #{Scale::Types.list.length}"
34
- end
35
-
36
- # exmaples:
37
- #
38
- # scale type Compact
39
- # => Scale::Types::Compact
40
- #
41
- # scale type SessionKeysPolkadot kusama
42
- # => nil
43
- #
44
- # scale type SessionKeysPolkadot kusama 1054
45
- # => Scale::Types::Struct_Of_AccountId_AccountId_AccountId_AccountId_AccountId_70247479160460
46
- #
47
- # scale type "UnappliedSlash<AccountId, BalanceOf>"
48
- # => Scale::Types::Struct_Of_AccountId_AccountId_Vec˂UnappliedSlashOther˃_Vec˂AccountId˃_Balance_70145184872260
49
- #
50
- desc "type TYPE_NAME SPEC_NAME SPEC_VERSION", "show type's ruby class"
51
- def type(type_name, spec_name = "default", spec_version = nil)
52
- Scale::TypeRegistry.instance.load(spec_name, spec_version)
53
- p Scale::Types.get(type_name)
54
- end
55
-
56
- # exmaples:
57
- #
58
- # scale decode Compact 0x0300000040
59
- # => #<Scale::Types::Compact:0x00007fc86b9d1198 @value=1073741824>
60
- desc "decode TYPE_NAME HEX SPEC_NAME SPEC_VERSION", "decode HEX string using TYPE_NAME"
61
- def decode(type_name, hex, spec_name = "default", spec_version = nil)
62
- Scale::TypeRegistry.instance.load(spec_name, spec_version)
63
- type = Scale::Types.get(type_name)
64
- scale_bytes = Scale::Bytes.new(hex)
65
- p type.decode(scale_bytes)
66
- end
67
-
68
- # exmaples:
69
- #
70
- # scale encode SessionIndex 2818
71
- # => #<Scale::Types::Compact:0x00007fc86b9d1198 @value=1073741824>
72
- desc "encode TYPE_NAME VALUE SPEC_NAME SPEC_VERSION", "encode value"
73
- def encode(type_name, value, spec_name = "default", spec_version = nil)
74
- Scale::TypeRegistry.instance.load(spec_name, spec_version)
75
- type = Scale::Types.get(type_name)
76
- p "0x" + type.new(value.to_i).encode
77
- end
78
43
 
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
44
+ types.uniq!
45
+ puts types
86
46
  end
87
47
  end
88
48
 
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