ciri 0.0.0 → 0.0.1

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.
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
@@ -22,8 +22,9 @@
22
22
  # THE SOFTWARE.
23
23
 
24
24
 
25
+ require 'ciri/actor'
26
+ require 'ciri/utils'
25
27
  require_relative 'rlpx'
26
- require_relative 'actor'
27
28
  require_relative 'protocol_io'
28
29
 
29
30
  module Ciri
@@ -49,6 +50,12 @@ module Ciri
49
50
  super()
50
51
  end
51
52
 
53
+ def to_s
54
+ @display_name ||= begin
55
+ Utils.data_to_hex(node_id.id)[0..8]
56
+ end
57
+ end
58
+
52
59
  def node_id
53
60
  @node_id ||= RLPX::NodeID.from_raw_id(@handshake.id)
54
61
  end
@@ -74,7 +81,7 @@ module Ciri
74
81
 
75
82
  def start_protocols
76
83
  @protocols.each do |protocol|
77
- protocol.start.(self, @protocol_io_hash[protocol.name])
84
+ protocol.start(self, @protocol_io_hash[protocol.name])
78
85
  end
79
86
  end
80
87
 
@@ -94,6 +101,7 @@ module Ciri
94
101
  end
95
102
 
96
103
  private
104
+
97
105
  def find_protocol_io_by_msg_code(code)
98
106
  @protocol_io_hash.values.find do |protocol_io|
99
107
  offset = protocol_io.offset
@@ -22,8 +22,6 @@
22
22
  # THE SOFTWARE.
23
23
 
24
24
 
25
- require_relative 'actor'
26
-
27
25
  module Ciri
28
26
  module DevP2P
29
27
 
@@ -31,12 +29,22 @@ module Ciri
31
29
  class Protocol
32
30
 
33
31
  attr_reader :name, :version, :length
34
- attr_accessor :node_info, :peer_info, :start
32
+ attr_accessor :node_info, :peer_info
35
33
 
36
34
  def initialize(name:, version:, length:)
37
35
  @name = name
38
36
  @version = version
39
37
  @length = length
38
+ @start = nil
39
+ end
40
+
41
+ def start=(start_proc)
42
+ @start = start_proc
43
+ end
44
+
45
+ def start(peer, io)
46
+ raise NotImplementedError.new('not set protocol start proc') unless @start
47
+ @start.call(peer, io)
40
48
  end
41
49
  end
42
50
 
@@ -21,7 +21,7 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
- require_relative 'actor'
24
+ require 'ciri/actor'
25
25
  require_relative 'rlpx/message'
26
26
 
27
27
  module Ciri
@@ -42,11 +42,14 @@ module Ciri
42
42
  @offset = offset
43
43
  @frame_io = frame_io
44
44
  @msg_queue = Queue.new
45
+ @mutex = Mutex.new
45
46
  end
46
47
 
47
48
  def send_data(code, data)
48
- msg = RLPX::Message.new(code: code, size: data.size, payload: data)
49
- write_msg(msg)
49
+ @mutex.synchronize do
50
+ msg = RLPX::Message.new(code: code, size: data.size, payload: data)
51
+ write_msg(msg)
52
+ end
50
53
  end
51
54
 
52
55
  def write_msg(msg)
@@ -25,4 +25,5 @@ require_relative 'rlpx/node'
25
25
  require_relative 'rlpx/message'
26
26
  require_relative 'rlpx/frame_io'
27
27
  require_relative 'rlpx/protocol_messages'
28
+ require_relative 'rlpx/protocol_handshake'
28
29
  require_relative 'rlpx/encryption_handshake'
@@ -69,7 +69,7 @@ module Ciri
69
69
  # xor
70
70
  signed = xor(token, nonce)
71
71
 
72
- signature = random_key.ecdsa_signature(signed)
72
+ signature = random_key.ecdsa_signature(signed).signature
73
73
  initiator_pubkey = private_key.raw_public_key[1..-1]
74
74
  AuthMsgV4.new(signature: signature, initiator_pubkey: initiator_pubkey, nonce: nonce, version: 4)
75
75
  end
@@ -218,4 +218,4 @@ module Ciri
218
218
 
219
219
  end
220
220
  end
221
- end
221
+ end
@@ -21,7 +21,7 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
- require 'ciri/rlp/serializable'
24
+ require 'ciri/rlp'
25
25
 
26
26
  module Ciri
27
27
  module DevP2P
@@ -31,13 +31,13 @@ module Ciri
31
31
  class Message
32
32
  include Ciri::RLP::Serializable
33
33
 
34
+ attr_accessor :received_at
35
+
34
36
  schema [
35
37
  {code: Integer},
36
38
  {size: Integer},
37
- :payload,
38
- :received_at
39
+ :payload
39
40
  ]
40
- default_data(received_at: nil)
41
41
  end
42
42
 
43
43
  end
@@ -24,10 +24,11 @@
24
24
 
25
25
  require 'concurrent'
26
26
  require 'forwardable'
27
+ require 'ciri/actor'
28
+ require 'ciri/utils/logger'
27
29
  require_relative 'rlpx/connection'
28
30
  require_relative 'rlpx/protocol_handshake'
29
31
  require_relative 'peer'
30
- require_relative 'actor'
31
32
 
32
33
  module Ciri
33
34
  module DevP2P
@@ -35,6 +36,7 @@ module Ciri
35
36
  # DevP2P Server
36
37
  # maintain connection, node discovery, rlpx handshake and protocols
37
38
  class Server
39
+ include Utils::Logger
38
40
  include RLPX
39
41
 
40
42
  MAX_ACTIVE_DIAL_TASKS = 16
@@ -47,12 +49,12 @@ module Ciri
47
49
  end
48
50
 
49
51
  attr_reader :handshake, :dial, :scheduler, :protocol_manage, :protocols
50
- attr_accessor :logger, :bootstrap_nodes
52
+ attr_accessor :bootstrap_nodes
51
53
 
52
- def initialize(private_key:, protocol_manage:, executor:)
54
+ def initialize(private_key:, protocol_manage:)
53
55
  @private_key = private_key
54
56
  @name = 'ciri'
55
- @scheduler = Scheduler.new(self, executor: executor)
57
+ @scheduler = Scheduler.new(self)
56
58
  @protocol_manage = protocol_manage
57
59
  @protocols = protocol_manage.protocols
58
60
  end
@@ -66,7 +68,6 @@ module Ciri
66
68
  @handshake = ProtocolHandshake.new(version: BASE_PROTOCOL_VERSION, name: @name, id: server_node_id.id, caps: caps)
67
69
  # start listen tcp
68
70
  @dial = Dial.new(self)
69
- @protocol_manage.executor ||= @scheduler.executor
70
71
  @protocol_manage.start
71
72
  @scheduler.start
72
73
  end
@@ -124,19 +125,20 @@ module Ciri
124
125
 
125
126
  class Scheduler
126
127
  include Actor
128
+ include Utils::Logger
127
129
 
128
130
  extend Forwardable
129
131
 
130
132
  attr_reader :server
131
- def_delegators :server, :logger
133
+ def_delegators :server
132
134
 
133
- def initialize(server, executor:)
135
+ def initialize(server)
134
136
  @server = server
135
137
  @queued_tasks = []
136
138
  @running_tasks = []
137
139
  @peers = {}
138
140
  # init actor
139
- super(executor: executor)
141
+ super()
140
142
  end
141
143
 
142
144
  # called by actor loop
@@ -168,11 +170,10 @@ module Ciri
168
170
  end
169
171
 
170
172
  private
173
+
171
174
  def add_peer(connection, handshake)
172
175
  server.protocol_handshake_checks(handshake)
173
176
  peer = Peer.new(connection, handshake, server.protocols)
174
- # set actor executor
175
- peer.executor = executor
176
177
  @peers[peer.node_id] = peer
177
178
  # run peer logic
178
179
  # do sub protocol handshake...
@@ -188,16 +189,16 @@ module Ciri
188
189
  # remove peer
189
190
  self << [:remove_peer, peer, exit_error]
190
191
  }
191
- logger.debug("add peer: #{peer}")
192
+ debug("add peer: #{peer}")
192
193
  end
193
194
 
194
195
  def remove_peer(peer, *args)
195
196
  error, * = args
196
- logger.debug("remove peer: #{peer}, error: #{error}")
197
+ debug("remove peer: #{peer}, error: #{error}")
197
198
  end
198
199
 
199
200
  def task_done(task, *args)
200
- logger.debug("task done: #{task.name}")
201
+ debug("task done: #{task.name}")
201
202
  end
202
203
 
203
204
  end
@@ -0,0 +1,33 @@
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 'actor'
25
+ require_relative 'eth/protocol_messages'
26
+ require_relative 'eth/protocol_manage'
27
+
28
+ module Ciri
29
+ # implement Ethereum Wire Protocol
30
+ # https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol
31
+ module Eth
32
+ end
33
+ end
@@ -0,0 +1,64 @@
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/chain'
25
+ require 'forwardable'
26
+ require_relative 'protocol_messages'
27
+
28
+ module Ciri
29
+ module Eth
30
+
31
+ # eth protocol peer
32
+ class Peer
33
+ attr_reader :io, :total_difficulty, :status
34
+
35
+ extend Forwardable
36
+
37
+ def_delegators :@peer, :to_s
38
+
39
+ def initialize(protocol_manage:, peer:, io:)
40
+ @protocol_manage = protocol_manage
41
+ @io = io
42
+ @total_difficulty = nil
43
+ @peer = peer
44
+ end
45
+
46
+ # do eth protocol handshake and return status
47
+ def handshake(network_id, total_difficulty, head_hash, genesis_hash)
48
+ status = Status.new(protocol_version: 63, network_id: network_id,
49
+ total_difficulty: total_difficulty, current_block: head_hash, genesis_block: genesis_hash)
50
+ io.send_data(Status::CODE, status.rlp_encode!)
51
+ msg = io.read_msg
52
+ @status = Status.rlp_decode!(msg.payload)
53
+ @total_difficulty = @status.total_difficulty
54
+ @status
55
+ end
56
+
57
+ def send_msg(msg_class, **data)
58
+ msg = msg_class.new(data)
59
+ io.send_data(msg_class::CODE, msg.rlp_encode!)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,122 @@
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/actor'
25
+ require 'ciri/chain'
26
+ require_relative 'peer'
27
+ require_relative 'synchronizer'
28
+
29
+ module Ciri
30
+ module Eth
31
+
32
+ # ProtocolManage
33
+ class ProtocolManage
34
+
35
+ MAX_RESPONSE_HEADERS = 10
36
+
37
+ include Ciri::Actor
38
+
39
+ attr_reader :protocols, :synchronizer, :chain
40
+
41
+ def initialize(protocols:, chain:)
42
+ @protocols = protocols
43
+ @peers = {}
44
+ @chain = chain
45
+
46
+ @synchronizer = Synchronizer.new(chain: chain)
47
+ super()
48
+ end
49
+
50
+ def protocols
51
+ @protocols.each {|p| p.start = proc {|peer, io| self << [:new_peer, peer, io]}}
52
+ @protocols
53
+ end
54
+
55
+ def start
56
+ # start syncing
57
+ synchronizer.start
58
+ super
59
+ end
60
+
61
+ # new peer come in
62
+ def new_peer(peer, io)
63
+ peer = Peer.new(protocol_manage: self, peer: peer, io: io)
64
+ peer.handshake(1, chain.total_difficulty, chain.head.get_hash, chain.genesis_hash)
65
+ @peers[peer] = true
66
+
67
+ # register peer to synchronizer
68
+ synchronizer << [:register_peer, peer]
69
+ # start handle peer messages
70
+ executor.post {handle_peer(peer)}
71
+ end
72
+
73
+ def handle_peer(peer)
74
+ handle_msg(peer, peer.io.read_msg) while true
75
+ end
76
+
77
+ def handle_msg(peer, msg)
78
+ case msg.code
79
+ when GetBlockHeaders::CODE
80
+ get_header_msg = GetBlockHeaders.rlp_decode(msg.payload)
81
+ hash_or_number = get_header_msg.hash_or_number
82
+
83
+ header = if hash_or_number.is_a?(Integer)
84
+ chain.get_header_by_number hash_or_number
85
+ else
86
+ chain.get_header hash_or_number
87
+ end
88
+ headers = []
89
+
90
+ if header
91
+ amount = [MAX_RESPONSE_HEADERS, get_header_msg.amount].min
92
+ # skip
93
+ get_header_msg.skip.times do
94
+ next_header = chain.get_header_by_number header.number + 1
95
+ break if next_header.nil? || next_header.parent_hash != header.get_hash
96
+ header = next_header
97
+ end
98
+ amount.times do
99
+ headers << header
100
+ next_header = chain.get_header_by_number header.number + 1
101
+ break if next_header.nil? || next_header.parent_hash != header.get_hash
102
+ header = next_header
103
+ end
104
+ header.reverse! if get_header_msg.reverse
105
+ end
106
+
107
+ headers_msg = BlockHeaders.new(headers: headers).rlp_encode!
108
+ peer.io.send_data(BlockHeaders::CODE, headers_msg)
109
+ when BlockHeaders::CODE
110
+ headers = BlockHeaders.rlp_decode!(msg.payload).headers
111
+ synchronizer << [:receive_headers, peer, headers] unless headers.empty?
112
+ when BlockBodies::CODE
113
+ bodies = BlockBodies.rlp_decode!(msg.payload).bodies
114
+ synchronizer << [:receive_bodies, peer, bodies] unless bodies.empty?
115
+ else
116
+ raise StandardError, "unknown code #{msg.code}, #{msg}"
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end