ciri 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +14 -0
  3. data/.rspec +2 -1
  4. data/.travis.yml +11 -4
  5. data/Gemfile.lock +3 -0
  6. data/README.md +44 -34
  7. data/Rakefile +47 -4
  8. data/ciri.gemspec +13 -12
  9. data/docker/Base +34 -0
  10. data/lib/ciri/actor.rb +223 -0
  11. data/lib/ciri/chain.rb +293 -0
  12. data/lib/ciri/chain/block.rb +47 -0
  13. data/lib/ciri/chain/header.rb +62 -0
  14. data/lib/ciri/chain/transaction.rb +145 -0
  15. data/lib/ciri/crypto.rb +58 -5
  16. data/lib/ciri/db/backend/memory.rb +68 -0
  17. data/lib/ciri/db/backend/rocks.rb +104 -0
  18. data/lib/ciri/db/backend/rocks_db.rb +278 -0
  19. data/lib/ciri/devp2p/peer.rb +10 -2
  20. data/lib/ciri/devp2p/protocol.rb +11 -3
  21. data/lib/ciri/devp2p/protocol_io.rb +6 -3
  22. data/lib/ciri/devp2p/rlpx.rb +1 -0
  23. data/lib/ciri/devp2p/rlpx/encryption_handshake.rb +1 -1
  24. data/lib/ciri/devp2p/rlpx/frame_io.rb +1 -1
  25. data/lib/ciri/devp2p/rlpx/message.rb +4 -4
  26. data/lib/ciri/devp2p/server.rb +14 -13
  27. data/lib/ciri/eth.rb +33 -0
  28. data/lib/ciri/eth/peer.rb +64 -0
  29. data/lib/ciri/eth/protocol_manage.rb +122 -0
  30. data/lib/ciri/eth/protocol_messages.rb +158 -0
  31. data/lib/ciri/eth/synchronizer.rb +188 -0
  32. data/lib/ciri/ethash.rb +123 -0
  33. data/lib/ciri/evm.rb +140 -0
  34. data/lib/ciri/evm/account.rb +50 -0
  35. data/lib/ciri/evm/block_info.rb +31 -0
  36. data/lib/ciri/evm/forks/frontier.rb +183 -0
  37. data/lib/ciri/evm/instruction.rb +92 -0
  38. data/lib/ciri/evm/machine_state.rb +81 -0
  39. data/lib/ciri/evm/op.rb +536 -0
  40. data/lib/ciri/evm/serialize.rb +60 -0
  41. data/lib/ciri/evm/sub_state.rb +64 -0
  42. data/lib/ciri/evm/vm.rb +379 -0
  43. data/lib/ciri/forks.rb +38 -0
  44. data/lib/ciri/forks/frontier.rb +43 -0
  45. data/lib/ciri/key.rb +7 -1
  46. data/lib/ciri/pow.rb +95 -0
  47. data/lib/ciri/rlp.rb +3 -53
  48. data/lib/ciri/rlp/decode.rb +100 -40
  49. data/lib/ciri/rlp/encode.rb +95 -34
  50. data/lib/ciri/rlp/serializable.rb +61 -91
  51. data/lib/ciri/types/address.rb +70 -0
  52. data/lib/ciri/types/errors.rb +36 -0
  53. data/lib/ciri/utils.rb +45 -13
  54. data/lib/ciri/utils/lib_c.rb +46 -0
  55. data/lib/ciri/utils/logger.rb +99 -0
  56. data/lib/ciri/utils/number.rb +67 -0
  57. data/lib/ciri/version.rb +1 -1
  58. metadata +67 -7
  59. data/lib/ciri/devp2p/actor.rb +0 -224
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require_relative 'forks/frontier'
25
+
26
+ module Ciri
27
+ module Forks
28
+
29
+ # Fork configure
30
+ ForkConfig = Struct.new(:cost_of_operation, :cost_of_memory, :intrinsic_gas_of_transaction, keyword_init: true)
31
+
32
+ def self.detect_fork(header: nil, number: nil)
33
+ number ||= header.number
34
+ Frontier.fork_config
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require 'ciri/evm/forks/frontier'
25
+
26
+ module Ciri
27
+ module Forks
28
+ module Frontier
29
+
30
+ class << self
31
+ def fork_config
32
+ ForkConfig.new(
33
+ cost_of_operation: proc {|vm| EVM::Forks::Frontier::Cost.cost_of_operation vm},
34
+ cost_of_memory: proc {|i| EVM::Forks::Frontier::Cost.cost_of_memory i},
35
+ intrinsic_gas_of_transaction: proc {|t| EVM::Forks::Frontier::Cost.intrinsic_gas_of_transaction t}
36
+ # transaction_klass: nil
37
+ )
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -74,8 +74,14 @@ module Ciri
74
74
  end
75
75
 
76
76
  private
77
+
77
78
  def secp256k1_key
78
- @secp256k1_key ||= Crypto.ensure_secp256k1_key(privkey: ec_key.private_key.to_s(2))
79
+ privkey = ec_key.private_key.to_s(2)
80
+ # some times below error will occurs, raise error with more detail
81
+ unless privkey.instance_of?(String) && privkey.size == 32
82
+ raise ArgumentError, "privkey must be composed of 32 bytes, #{bytes}: #{privkey.size} privkey: #{Utils.data_to_hex privkey}"
83
+ end
84
+ @secp256k1_key ||= Crypto.ensure_secp256k1_key(privkey: privkey)
79
85
  end
80
86
  end
81
87
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+
24
+ require 'ciri/utils'
25
+ require_relative 'ethash'
26
+ require 'lru_redux'
27
+ require 'concurrent'
28
+
29
+ module Ciri
30
+
31
+ # POW
32
+ # see py-evm https://github.com/ethereum/py-evm/blob/026553da69bbea314fe26c8c34d453f66bfb4d30/evm/consensus/pow.py
33
+ module POW
34
+
35
+ extend self
36
+
37
+ class Error < StandardError
38
+ end
39
+ class InvalidError < Error
40
+ end
41
+
42
+ class GivingUpError < Error
43
+ end
44
+
45
+ # thread safe caches
46
+ @cache_seeds = Concurrent::Array.new(['\x00'.b * 32])
47
+ @cache_by_seed = LruRedux::ThreadSafeCache.new(10)
48
+
49
+ def get_cache(block_number)
50
+ epoch = block_number / Ethash::EPOCH_LENGTH
51
+ while @cache_seeds.size <= epoch
52
+ @cache_seeds.append(Utils.sha3(@cache_seeds[-1]))
53
+ end
54
+
55
+ seed = @cache_seeds[epoch]
56
+
57
+ @cache_by_seed.getset(seed) do
58
+ Ethash.mkcache_bytes(block_number)
59
+ end
60
+ end
61
+
62
+ def check_pow(block_number, mining_hash, mix_hash, nonce_bytes, difficulty)
63
+ raise ArgumentError.new "mix_hash.length must equal to 32" if mix_hash.size != 32
64
+ raise ArgumentError.new "mining_hash.length must equal to 32" if mining_hash.size != 32
65
+ raise ArgumentError.new "nonce.length must equal to 8" if nonce_bytes.size != 8
66
+
67
+ cache = get_cache(block_number)
68
+ out_mix_hash, out_result = Ethash.hashimoto_light(block_number, cache, mining_hash, Utils.big_endian_decode(nonce_bytes))
69
+
70
+ if out_mix_hash != mix_hash
71
+ raise InvalidError.new("mix hash mismatch; #{Utils.data_to_hex(out_mix_hash)} != #{Utils.data_to_hex(mix_hash)}")
72
+ end
73
+
74
+ result = Utils.big_endian_decode(out_result)
75
+ unless result < 2 ** 256 / difficulty
76
+ raise InvalidError.new("difficulty not enough, need difficulty #{difficulty}, but result #{result}")
77
+ end
78
+ end
79
+
80
+ MAX_TEST_MINE_ATTEMPTS = 1000
81
+
82
+ def mine_pow_nonce(block_number, mining_hash, difficulty)
83
+ cache = get_cache(block_number)
84
+ MAX_TEST_MINE_ATTEMPTS.times do |nonce|
85
+ out_mix_hash, out_result = Ethash.hashimoto_light(block_number, cache, mining_hash, nonce)
86
+ result = Utils.big_endian_decode(out_result)
87
+ result_cap = 2 ** 256 / difficulty
88
+ return [out_mix_hash, Utils.big_endian_encode(nonce).rjust(8, "\x00")] if result <= result_cap
89
+ end
90
+
91
+ raise GivingUpError.new("tries too many times, giving up")
92
+ end
93
+
94
+ end
95
+ end
@@ -27,62 +27,12 @@ require_relative 'rlp/serializable'
27
27
 
28
28
  module Ciri
29
29
  module RLP
30
+
30
31
  class InvalidValueError < StandardError
31
32
  end
32
33
 
33
- class << self
34
-
35
- # Decode input from rlp encoding, only produce string or array
36
- #
37
- # Examples:
38
- #
39
- # Ciri::RLP.decode(input)
40
- #
41
- def decode(input, type = nil)
42
- output = Decode.decode(input)
43
- if type
44
- output = decode_with_type(output, type)
45
- end
46
- output
47
- end
48
-
49
- # Encode input to rlp encoding, only allow string or array
50
- #
51
- # Examples:
52
- #
53
- # Ciri::RLP.encode("hello world")
54
- #
55
- def encode(input, type = nil)
56
- if type
57
- input = encode_with_type(input, type)
58
- end
59
- Encode.encode(input)
60
- end
34
+ extend Encode
35
+ extend Decode
61
36
 
62
- # Use this method before RLP.encode, this method encode ruby objects to rlp friendly format, string or array.
63
- # see Ciri::RLP::Serializable::TYPES for supported types
64
- #
65
- # Examples:
66
- #
67
- # item = Ciri::RLP.encode_with_type(number, :int, zero: "\x00".b)
68
- # encoded_text = Ciri::RLP.encode(item)
69
- #
70
- def encode_with_type(item, type, zero: '')
71
- Serializable.encode_with_type(item, type, zero: zero)
72
- end
73
-
74
- # Use this method after RLP.decode, decode values from string or array to specific types
75
- # see Ciri::RLP::Serializable::TYPES for supported types
76
- #
77
- # Examples:
78
- #
79
- # item = Ciri::RLP.decode(encoded_text)
80
- # number = Ciri::RLP.decode_with_type(item, :int)
81
- #
82
- def decode_with_type(item, type)
83
- Serializable.decode_with_type(item, type)
84
- end
85
-
86
- end
87
37
  end
88
38
  end
@@ -27,55 +27,115 @@ module Ciri
27
27
  module RLP
28
28
  module Decode
29
29
 
30
- class InvalidInput < StandardError
30
+ # Decode input from rlp encoding, only produce string or array
31
+ #
32
+ # Examples:
33
+ #
34
+ # Ciri::RLP.decode(input)
35
+ #
36
+ def decode(input, type = Raw)
37
+ decode_with_type(input, type)
31
38
  end
32
39
 
33
- class << self
34
- def decode(input)
35
- s = StringIO.new(input).binmode
40
+ # Use this method after RLP.decode, decode values from string or array to specific types
41
+ # see Ciri::RLP::Serializable::TYPES for supported types
42
+ #
43
+ # Examples:
44
+ #
45
+ # item = Ciri::RLP.decode(encoded_text)
46
+ # decode_with_type(item, Integer)
47
+ #
48
+ def decode_with_type(s, type)
49
+ s = StringIO.new(s) if s.is_a?(String)
50
+ if type == Integer
51
+ item = s.read(1)
52
+ if item.nil?
53
+ raise InvalidValueError.new "invalid bool value nil"
54
+ elsif item == "\x80".b || item.empty?
55
+ 0
56
+ elsif item.ord < 0x80
57
+ item.ord
58
+ else
59
+ size = item[0].ord - 0x80
60
+ Ciri::Utils.big_endian_decode(s.read(size))
61
+ end
62
+ elsif type == Bool
63
+ item = s.read(1)
64
+ if item == Bool::ENCODED_TRUE
65
+ true
66
+ elsif item == Bool::ENCODED_FALSE
67
+ false
68
+ else
69
+ raise InvalidValueError.new "invalid bool value #{item}"
70
+ end
71
+ elsif type.is_a?(Class) && type < Serializable
72
+ type.rlp_decode!(s)
73
+ elsif type.is_a?(Array)
74
+ decode_list(s) do |list, s2|
75
+ until s2.eof?
76
+ list << decode_with_type(s2, type[0])
77
+ end
78
+ end
79
+ elsif type == Raw
36
80
  decode_stream(s)
81
+ else
82
+ raise RLP::InvalidValueError.new "unknown type #{type}"
37
83
  end
84
+ rescue
85
+ STDERR.puts "when decoding #{s} into #{type}"
86
+ raise
87
+ end
38
88
 
39
- private
40
- def decode_stream(s)
41
- c = s.read(1)
42
- case c.ord
43
- when 0x00..0x7f
44
- c
45
- when 0x80..0xb7
46
- length = c.ord - 0x80
47
- s.read(length)
48
- when 0xb8..0xbf
49
- length_binary = s.read(c.ord - 0xb7)
50
- length = int_from_binary(length_binary)
51
- s.read(length)
52
- when 0xc0..0xf7
53
- length = c.ord - 0xc0
54
- s2 = StringIO.new s.read(length)
55
- list = []
56
- until s2.eof?
57
- list << decode_stream(s2)
58
- end
59
- list
60
- when 0xf8..0xff
61
- length_binary = s.read(c.ord - 0xf7)
62
- length = int_from_binary(length_binary)
63
- s2 = StringIO.new s.read(length)
64
- list = []
65
- until s2.eof?
66
- list << decode_stream(s2)
67
- end
68
- list
69
- else
70
- raise InvalidInput.new("invalid char #{c}")
71
- end
72
- end
89
+ protected
90
+
91
+ def decode_list(s, first_char = nil, &decoder)
92
+ s = StringIO.new(s) if s.is_a?(String)
93
+ c = first_char || s.read(1)
94
+ list = []
73
95
 
74
- def int_from_binary(input)
75
- Ciri::Utils.big_endian_decode(input)
96
+ sub_s = case c.ord
97
+ when 0xc0..0xf7
98
+ length = c.ord - 0xc0
99
+ s.read(length)
100
+ when 0xf8..0xff
101
+ length_binary = s.read(c.ord - 0xf7)
102
+ length = int_from_binary(length_binary)
103
+ s.read(length)
104
+ else
105
+ raise InvalidValueError.new("invalid char #{c}")
106
+ end
107
+
108
+ decoder.call(list, StringIO.new(sub_s))
109
+ list
110
+ end
111
+
112
+ private
113
+
114
+ def decode_stream(s)
115
+ c = s.read(1)
116
+ case c.ord
117
+ when 0x00..0x7f
118
+ c
119
+ when 0x80..0xb7
120
+ length = c.ord - 0x80
121
+ s.read(length)
122
+ when 0xb8..0xbf
123
+ length_binary = s.read(c.ord - 0xb7)
124
+ length = int_from_binary(length_binary)
125
+ s.read(length)
126
+ else
127
+ decode_list(s, c) do |list, s2|
128
+ until s2.eof?
129
+ list << decode_stream(s2)
130
+ end
131
+ end
76
132
  end
133
+ end
77
134
 
135
+ def int_from_binary(input)
136
+ Ciri::Utils.big_endian_decode(input)
78
137
  end
138
+
79
139
  end
80
140
  end
81
141
  end
@@ -28,52 +28,113 @@ module Ciri
28
28
  class InputOverflow < StandardError
29
29
  end
30
30
 
31
- class << self
31
+ # Encode input to rlp encoding, only allow string or array
32
+ #
33
+ # Examples:
34
+ #
35
+ # Ciri::RLP.encode("hello world")
36
+ #
37
+ def encode(input, type = Raw)
38
+ encode_with_type(input, type)
39
+ end
32
40
 
33
- def encode(input)
34
- result = if input.is_a?(String)
35
- encode_string(input)
36
- elsif input.is_a?(Array)
37
- encode_list(input)
38
- else
39
- raise ArgumentError.new('input must be a String or Array')
40
- end
41
- result.b
41
+ def encode_simple(input)
42
+ if input.is_a?(Array)
43
+ encode_list(input) {|i| encode_simple(i)}
44
+ elsif input.is_a?(Integer)
45
+ encode(input, Integer)
46
+ elsif input.is_a?(Serializable)
47
+ input.rlp_encode!
48
+ else
49
+ encode(input)
42
50
  end
51
+ end
43
52
 
44
- private
45
- def encode_string(input)
46
- length = input.length
47
- if length == 1 && input.ord < 0x80
48
- input
49
- elsif length < 56
50
- to_binary(0x80 + length) + input
51
- elsif length < 256 ** 8
52
- binary_length = to_binary(length)
53
- to_binary(0xb7 + binary_length.size) + binary_length + input
53
+ # Use this method before RLP.encode, this method encode ruby objects to rlp friendly format, string or array.
54
+ # see Ciri::RLP::Serializable::TYPES for supported types
55
+ #
56
+ # Examples:
57
+ #
58
+ # item = Ciri::RLP.encode_with_type(number, :int, zero: "\x00".b)
59
+ # encoded_text = Ciri::RLP.encode(item)
60
+ #
61
+ def encode_with_type(item, type, zero: '')
62
+ if type == Integer
63
+ if item == 0
64
+ "\x80".b
65
+ elsif item < 0x80
66
+ Ciri::Utils.big_endian_encode(item, zero)
54
67
  else
55
- raise InputOverflow.new("input length #{input.size} is too long")
68
+ buf = Ciri::Utils.big_endian_encode(item, zero)
69
+ [0x80 + buf.size].pack("c*") + buf
56
70
  end
71
+ elsif type == Bool
72
+ item ? Bool::ENCODED_TRUE : Bool::ENCODED_FALSE
73
+ elsif type.is_a?(Class) && type < Serializable
74
+ item.rlp_encode!
75
+ elsif type.is_a?(Array)
76
+ if type.size == 1 # array type
77
+ encode_list(item) {|i| encode_with_type(i, type[0])}
78
+ else # unknown
79
+ raise RLP::InvalidValueError.new "type size should be 1, got #{type}"
80
+ end
81
+ elsif type == Raw
82
+ encode_raw(item)
83
+ else
84
+ raise RLP::InvalidValueError.new "unknown type #{type}"
57
85
  end
86
+ rescue
87
+ STDERR.puts "when encoding #{Utils.data_to_hex item.to_s} into #{type}"
88
+ raise
89
+ end
58
90
 
59
- def encode_list(input)
60
- output = input.map {|item| encode(item)}.join
61
- length = output.length
62
- if length < 56
63
- to_binary(0xc0 + length) + output
64
- elsif length < 256 ** 8
65
- binary_length = to_binary(length)
66
- to_binary(0xf7 + binary_length.size) + binary_length + output
67
- else
68
- raise InputOverflow.new("input length #{input.size} is too long")
69
- end
91
+ protected
92
+
93
+ def encode_raw(input)
94
+ result = if input.is_a?(String)
95
+ encode_string(input)
96
+ elsif input.is_a?(Array)
97
+ encode_list(input) {|item| encode(item)}
98
+ else
99
+ raise ArgumentError.new("input must be a String or Array, #{input.inspect}")
100
+ end
101
+ result.b
102
+ end
103
+
104
+ def encode_string(input)
105
+ length = input.length
106
+ if length == 1 && input.ord < 0x80
107
+ input
108
+ elsif length < 56
109
+ to_binary(0x80 + length) + input
110
+ elsif length < 256 ** 8
111
+ binary_length = to_binary(length)
112
+ to_binary(0xb7 + binary_length.size) + binary_length + input
113
+ else
114
+ raise InputOverflow.new("input length #{input.size} is too long")
70
115
  end
116
+ end
71
117
 
72
- def to_binary(n)
73
- Ciri::Utils.big_endian_encode(n)
118
+ def encode_list(input, &encoder)
119
+ input ||= [] # allow nil list
120
+ output = encoder ? input.map {|item| encoder.call(item)}.join : input.join
121
+ length = output.length
122
+ if length < 56
123
+ to_binary(0xc0 + length) + output
124
+ elsif length < 256 ** 8
125
+ binary_length = to_binary(length)
126
+ to_binary(0xf7 + binary_length.size) + binary_length + output
127
+ else
128
+ raise InputOverflow.new("input length #{input.size} is too long")
74
129
  end
130
+ end
131
+
132
+ private
75
133
 
134
+ def to_binary(n)
135
+ Ciri::Utils.big_endian_encode(n)
76
136
  end
137
+
77
138
  end
78
139
  end
79
140
  end