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
@@ -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