cryptocoin 0.0.1b

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +85 -0
  7. data/Rakefile +2 -0
  8. data/cryptocoin.gemspec +24 -0
  9. data/lib/cryptocoin/core_ext/integer.rb +24 -0
  10. data/lib/cryptocoin/core_ext/string.rb +30 -0
  11. data/lib/cryptocoin/digest.rb +36 -0
  12. data/lib/cryptocoin/merkle_tree.rb +78 -0
  13. data/lib/cryptocoin/network/bitcoin.rb +9 -0
  14. data/lib/cryptocoin/network/dogecoin.rb +9 -0
  15. data/lib/cryptocoin/network/litecoin.rb +9 -0
  16. data/lib/cryptocoin/network.rb +53 -0
  17. data/lib/cryptocoin/protocol/block_header.rb +58 -0
  18. data/lib/cryptocoin/protocol/inventory_vector.rb +28 -0
  19. data/lib/cryptocoin/protocol/message/addr.rb +29 -0
  20. data/lib/cryptocoin/protocol/message/alert.rb +0 -0
  21. data/lib/cryptocoin/protocol/message/block.rb +13 -0
  22. data/lib/cryptocoin/protocol/message/getaddr.rb +17 -0
  23. data/lib/cryptocoin/protocol/message/getblocks.rb +44 -0
  24. data/lib/cryptocoin/protocol/message/getdata.rb +31 -0
  25. data/lib/cryptocoin/protocol/message/getheaders.rb +44 -0
  26. data/lib/cryptocoin/protocol/message/headers.rb +30 -0
  27. data/lib/cryptocoin/protocol/message/inv.rb +31 -0
  28. data/lib/cryptocoin/protocol/message/mempool.rb +16 -0
  29. data/lib/cryptocoin/protocol/message/notfound.rb +31 -0
  30. data/lib/cryptocoin/protocol/message/ping.rb +24 -0
  31. data/lib/cryptocoin/protocol/message/pong.rb +24 -0
  32. data/lib/cryptocoin/protocol/message/reject.rb +54 -0
  33. data/lib/cryptocoin/protocol/message/tx.rb +11 -0
  34. data/lib/cryptocoin/protocol/message/verack.rb +16 -0
  35. data/lib/cryptocoin/protocol/message/version.rb +59 -0
  36. data/lib/cryptocoin/protocol/message.rb +27 -0
  37. data/lib/cryptocoin/protocol/net_addr.rb +55 -0
  38. data/lib/cryptocoin/protocol/packet.rb +74 -0
  39. data/lib/cryptocoin/protocol/var_len_int.rb +85 -0
  40. data/lib/cryptocoin/protocol/var_len_str.rb +18 -0
  41. data/lib/cryptocoin/protocol.rb +8 -0
  42. data/lib/cryptocoin/script/op_code/constants.rb +157 -0
  43. data/lib/cryptocoin/script/op_code/functions.rb +515 -0
  44. data/lib/cryptocoin/script/op_code.rb +59 -0
  45. data/lib/cryptocoin/script.rb +234 -0
  46. data/lib/cryptocoin/structure/address.rb +64 -0
  47. data/lib/cryptocoin/structure/block.rb +109 -0
  48. data/lib/cryptocoin/structure/key_pair.rb +57 -0
  49. data/lib/cryptocoin/structure/merkle_branch.rb +37 -0
  50. data/lib/cryptocoin/structure/transaction/input.rb +80 -0
  51. data/lib/cryptocoin/structure/transaction/output.rb +49 -0
  52. data/lib/cryptocoin/structure/transaction.rb +94 -0
  53. data/lib/cryptocoin/version.rb +3 -0
  54. data/lib/cryptocoin.rb +29 -0
  55. data/spec/script_spec.rb +42 -0
  56. data/spec/spec_helper.rb +20 -0
  57. metadata +145 -0
@@ -0,0 +1,44 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Getheaders
5
+ attr_reader :digest_count
6
+
7
+ def parse_from_raw(payload)
8
+ block_locator_digests, i = [], 0
9
+ version = payload[0..3]
10
+ digest_count = Cryptocoin::Protocol::VarLenInt.parse_from_raw(payload[4..-1])
11
+ digest_count.times do
12
+ c = digest_count.raw.bytesize+i*32
13
+ block_locator_digests.push(payload[c..c+31])
14
+ i+=1
15
+ end
16
+ digest_stop = payload[5+i*32..-1]
17
+ end
18
+
19
+ def initialize(version, digest_count, block_locator_digests, digest_stop)
20
+ @version_raw = version
21
+ @digest_count = digest_count
22
+ @block_locator_digests_raw = block_locator_digests
23
+ @digest_stop_raw = digest_stop
24
+ end
25
+
26
+ def version
27
+ @version ||= @version_raw.unpack('L')[0]
28
+ end
29
+
30
+ def block_locator_digests
31
+ @block_locator_digests ||= @block_locator_digests_raw.map{|e| e.unpack('H*')[0] }
32
+ end
33
+
34
+ def digest_stop
35
+ @digest_stop ||= @digest_stop_raw.unpack('H*')[0]
36
+ end
37
+
38
+ def raw
39
+ @version_raw + @digest_count.raw + @block_locator_digests_raw.join + @digest_stop_raw
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ require 'cryptocoin/protocol/block_header'
2
+
3
+ module Cryptocoin
4
+ module Protocol
5
+ class Message
6
+ class Headers
7
+ attr_reader :count, :headers
8
+ def self.parse_from_raw(payload)
9
+ headers, i = [], 0
10
+ count = Cryptocoin::Protocol::VarLenInt.parse_from_raw(payload)
11
+ count.times do
12
+ c = count.raw.bytesize + i*81
13
+ headers.push(Cryptocoin::Protocol::BlockHeader.parse_from_raw(payload[c..c+80]))
14
+ i+=1
15
+ end
16
+ self.new(count, headers)
17
+ end
18
+
19
+ def initialize(count, headers)
20
+ @count = count
21
+ @headers = headers
22
+ end
23
+
24
+ def raw
25
+ @count.raw + @headers.map{|e| e.raw}.join
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ require 'cryptocoin/protocol/inventory_vector'
2
+ require 'cryptocoin/protocol/var_len_int'
3
+
4
+ module Cryptocoin
5
+ module Protocol
6
+ class Message
7
+ class Inv
8
+ def self.parse_from_raw(payload)
9
+ inventory = []
10
+ i = 0
11
+ count = Cryptocoin::Protocol::VarLenInt.parse_from_raw(payload)
12
+ count.times do
13
+ c = count.raw.bytesize+i*36
14
+ inventory.push(Cryptocoin::Protocol::InventoryVector.parse_from_raw(payload[c..c+35]))
15
+ i+=1
16
+ end
17
+ self.new(count, inventory)
18
+ end
19
+
20
+ def initialize(count, inventory)
21
+ @count_raw = count
22
+ @inventory = inventory
23
+ end
24
+
25
+ def raw
26
+ @count_raw + @inventory.map{|e| e.raw}.join
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Mempool
5
+ def self.parse_from_raw(payload)
6
+ # https://en.bitcoin.it/wiki/Protocol_specification#mempool
7
+ # No data is sent with this message
8
+ self.new
9
+ end
10
+ def raw
11
+ nil
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ require 'cryptocoin/protocol/inventory_vector'
2
+ require 'cryptocoin/protocol/var_len_int'
3
+
4
+ module Cryptocoin
5
+ module Protocol
6
+ class Message
7
+ class Notfound
8
+ def self.parse_from_raw(payload)
9
+ inventory = []
10
+ i = 0
11
+ count = Cryptocoin::Protocol::VarLenInt.parse_from_raw(payload)
12
+ count.times do
13
+ c = count.raw.bytesize+i*36
14
+ inventory.push(Cryptocoin::Protocol::InventoryVector.parse_from_raw(payload[c..c+35]))
15
+ i+=1
16
+ end
17
+ self.new(count, inventory)
18
+ end
19
+
20
+ def initialize(count, inventory)
21
+ @count_raw = count
22
+ @inventory = inventory
23
+ end
24
+
25
+ def raw
26
+ @count_raw + @inventory.map{|e| e.raw}.join
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Ping
5
+ def self.parse_from_raw(payload)
6
+ nonce = payload
7
+ self.new(nonce)
8
+ end
9
+
10
+ def initialize(nonce)
11
+ @nonce_raw = nonce
12
+ end
13
+
14
+ def nonce
15
+ @nonce ||= @nonce_raw.unpack('Q')[0]
16
+ end
17
+
18
+ def raw
19
+ @nonce_raw
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Pong
5
+ def self.parse_from_raw(payload)
6
+ nonce = payload
7
+ self.new(nonce)
8
+ end
9
+
10
+ def initialize(nonce)
11
+ @nonce_raw = nonce
12
+ end
13
+
14
+ def nonce
15
+ @nonce ||= @nonce_raw.unpack('Q')[0]
16
+ end
17
+
18
+ def raw
19
+ @nonce_raw
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'cryptocoin/protocol/var_len_str'
2
+
3
+ module Cryptocoin
4
+ module Protocol
5
+ class Message
6
+ class Reject
7
+ attr_reader :message, :reason
8
+
9
+ def self.parse_from_raw(payload)
10
+ message = Cryptocoin::Protocol::VarLenStr.parse_from_raw(payload)
11
+ c = message.raw.bytesize
12
+ ccode = payload[c..c]
13
+ reason = Cryptocoin::Protocol::VarLenStr.parse_from_raw(payload[c+1..-1])
14
+ self.new(message, ccode, reason)
15
+ end
16
+
17
+ def initialize(message, ccode, reason)
18
+ @message = message
19
+ @ccode_raw = ccode
20
+ @reason = reason
21
+ end
22
+
23
+ def ccode
24
+ @ccode ||= @ccode_raw.unpack('H*')[0]
25
+ end
26
+
27
+ def ccode_name
28
+ case ccode
29
+ when '1'
30
+ 'REJECT_MALFORMED'
31
+ when '10'
32
+ 'REJECT_INVALID'
33
+ when '11'
34
+ 'REJECT_OBSOLETE'
35
+ when '12'
36
+ 'REJECT_DUPLICATE'
37
+ when '40'
38
+ 'REJECT_NONSTANDARD'
39
+ when '41'
40
+ 'REJECT_DUST'
41
+ when '42'
42
+ 'REJECT_INSUFFICIENTFEE'
43
+ when '43'
44
+ 'REJECT_CHECKPOINT'
45
+ end
46
+ end
47
+
48
+ def raw
49
+ @message.raw + @ccode_raw + @reason.raw
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Tx
5
+ def self.parse_from_raw(payload)
6
+ Cryptocoin::Structure::Transaction.parse_from_raw(payload)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Verack
5
+ def self.parse_from_raw(payload)
6
+ # https://en.bitcoin.it/wiki/Protocol_specification#verack
7
+ # Has no payload
8
+ self.new
9
+ end
10
+ def raw
11
+ nil
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class Message
4
+ class Version
5
+ def self.parse_from_raw(payload)
6
+ version = payload[0..3]
7
+ services = payload[4..11]
8
+ timestamp = payload[12..19]
9
+ address_received = Cryptocoin::Protocol::NetAddr.parse_from_raw(payload[20..45])
10
+ address_from = Cryptocoin::Protocol::NetAddr.parse_from_raw(payload[46..71])
11
+ nonce = payload[71..79]
12
+ user_agent = Cryptocoin::Protocol::VarLenStr.parse_from_raw(payload[80..-1])
13
+ start_height = payload[-5..-2]
14
+ relay = payload[-1]
15
+ end
16
+
17
+ def initialize(version, services, timestamp, address_received, address_from, nonce, user_agent, start_height, relay)
18
+ @version_raw = version
19
+ @services_raw = services
20
+ @timestamp_raw = timestamp
21
+ @address_received = address_received
22
+ @address_from = address_from
23
+ @nonce_raw = nonce
24
+ @user_agent = user_agent
25
+ @start_height_raw = start_height
26
+ @relay_raw = relay
27
+ end
28
+
29
+ def version
30
+ @version ||= @version_raw.unpack('l')[0]
31
+ end
32
+
33
+ def services
34
+ @services ||= @services_raw.unpack('Q')[0]
35
+ end
36
+
37
+ def timestamp
38
+ @timestamp ||= @timestamp_raw.unpack('q')[0]
39
+ end
40
+
41
+ def nonce
42
+ @nonce ||= @nonce_raw.unpack('Q')[0]
43
+ end
44
+
45
+ def start_height
46
+ @start_height ||= @start_height_raw.unpack('l')[0]
47
+ end
48
+
49
+ def relay
50
+ @relay ||= @relay_raw.unpack('c')[0]
51
+ end
52
+
53
+ def raw
54
+ @version_raw + @services_raw + @timestamp_raw + @address_received.raw + @address_from.raw + @nonce_raw + @user_agent.raw + @start_height_raw + @relay_raw
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,27 @@
1
+ require 'cryptocoin/protocol/message/addr'
2
+ require 'cryptocoin/protocol/message/alert'
3
+ require 'cryptocoin/protocol/message/block'
4
+ require 'cryptocoin/protocol/message/getaddr'
5
+ require 'cryptocoin/protocol/message/getblocks'
6
+ require 'cryptocoin/protocol/message/getdata'
7
+ require 'cryptocoin/protocol/message/getheaders'
8
+ require 'cryptocoin/protocol/message/headers'
9
+ require 'cryptocoin/protocol/message/inv'
10
+ require 'cryptocoin/protocol/message/mempool'
11
+ require 'cryptocoin/protocol/message/notfound'
12
+ require 'cryptocoin/protocol/message/ping'
13
+ require 'cryptocoin/protocol/message/pong'
14
+ require 'cryptocoin/protocol/message/reject'
15
+ require 'cryptocoin/protocol/message/tx'
16
+ require 'cryptocoin/protocol/message/verack'
17
+ require 'cryptocoin/protocol/message/version'
18
+
19
+ module Cryptocoin
20
+ module Protocol
21
+ class Message
22
+ def self.parse(command, payload)
23
+ Cryptocoin::Protocol::Message.const_get(command.capitalize).parse_from_raw(payload)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,55 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class NetAddr
4
+ def self.parse_from_raw(raw)
5
+ timestamp = raw[0..3]
6
+ services = raw[4..11]
7
+ address = raw[12..27]
8
+ port = raw[28..29]
9
+ self.new(timestamp, services, address, port)
10
+ end
11
+
12
+ def initialize(timestamp, services, address, port)
13
+ @timestamp_raw = timestamp
14
+ @services_raw = services
15
+ @address_raw = address
16
+ @port_raw = port
17
+ end
18
+
19
+ def timestamp
20
+ @timestamp ||= @timestamp_raw.unpack('L')[0]
21
+ end
22
+
23
+ def services
24
+ @services ||= @services_raw.unpack('Q')[0]
25
+ end
26
+
27
+ def address
28
+ return @address if @address
29
+ address = @address_raw.unpack('H*')[0]
30
+ if address[0..11] == "000000000000" # IPv4 address
31
+ address = address[12..-1]
32
+ @address = address.each_char.each_slice(2).map{|e| e.join.to_i(16)}.join('.')
33
+ @address_version = 4
34
+ else
35
+ @address = address.each_char.each_slice(4).map{|e| e.join}.join(':')
36
+ @address_version = 6
37
+ end
38
+ @address
39
+ end
40
+
41
+ def port
42
+ @port ||= @port_raw.unpack('S')[0]
43
+ end
44
+
45
+ def address_with_port
46
+ return "#{@address}:#{@port}" if @address_version == 4
47
+ "[#{@address}]:#{@port}"
48
+ end
49
+
50
+ def raw
51
+ @timestamp_raw + @services_raw + @address_raw + @port_raw
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,74 @@
1
+ require 'digest/sha2'
2
+ require 'cryptocoin/protocol/message'
3
+
4
+ module Cryptocoin
5
+ module Protocol
6
+ class Packet
7
+ attr_reader :message, :network
8
+
9
+ def self.parse_from_raw(raw)
10
+ magic = raw[0..4]
11
+ command = raw[5..16]
12
+ payload_length = raw[17..20]
13
+ payload_checksum = raw[21..24]
14
+ payload = raw[25..-1]
15
+ message = Cryptocoin::Protocol::Message.parse(command, payload)
16
+ end
17
+
18
+ def self.parse_from_io
19
+ magic = io.read(4)
20
+ command = io.read(12)
21
+ payload_length = io.read(4)
22
+ payload_checksum = io.read(4)
23
+ payload = io.read
24
+ message = Cryptocoin::Protocol::Message.parse(command, payload)
25
+ end
26
+
27
+ def initialize(magic, command, payload_length, payload_checksum, message, network)
28
+ @magic_raw = magic
29
+ @command_raw = command
30
+ @payload_length_raw = payload_length
31
+ @payload_checksum_raw = payload_checksum
32
+ @message = message
33
+ @network = network
34
+ end
35
+
36
+ def magic
37
+ @magic_raw.unpack('L')[0]
38
+ end
39
+
40
+ def command
41
+ @command_raw.unpack('a*')[0]
42
+ end
43
+
44
+ def payload_length
45
+ @payload_length_raw.unpack('L')[0]
46
+ end
47
+
48
+ def payload_checksum
49
+ @payload_checksum_raw.unpack('L')[0]
50
+ end
51
+
52
+ def valid?
53
+ valid_checksum and valid_magic and valid_message
54
+ end
55
+
56
+ def raw
57
+ @magic_raw + @command_raw + @payload_length_raw + @payload_checksum_raw + @message.raw
58
+ end
59
+
60
+ private
61
+ def valid_checksum
62
+ Cryptocoin::Digest.new(@message.raw, :binary).sha256[0..4] == @payload_checksum_raw
63
+ end
64
+
65
+ def valid_magic
66
+ magic == network.magic
67
+ end
68
+
69
+ def valid_message
70
+ @message.valid?
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,85 @@
1
+ module Cryptocoin
2
+ module Protocol
3
+ class VarLenInt
4
+ # https://github.com/andrew12/bitcoin-ruby/blob/master/lib/bitcoin.rb#L132
5
+ # TODO: test this implementation
6
+ def self.from_int(i)
7
+ if i < -0xffffffffffffffff
8
+ return ArgumentError, "Unrepresentable"
9
+ elsif i < 0
10
+ top_32 = ((-i) & 0xffffffff00000000) >> 32
11
+ btm_32 = (-i) & 0x00000000ffffffff
12
+ return self.new([0xff, top_32, btm_32].pack("CVV"))
13
+ elsif i <= 0xfc
14
+ return self.new([i].pack('C'))
15
+ elsif i <= 0xffff
16
+ return self.new([0xfd, i].pack("Cv"))
17
+ elsif i <= 0xffffffff
18
+ return self.new([0xfe, i].pack("CV"))
19
+ else
20
+ return ArgumentError, "Unrepresentable"
21
+ end
22
+ end
23
+
24
+ def self.parse_from_io(io)
25
+ i = io.read(1)
26
+ j = i.unpack('C')[0]
27
+ case j
28
+ when 0xfd
29
+ self.new(i + io.read(2))
30
+ when 0xfe
31
+ self.new(i + io.read(4))
32
+ when 0xff
33
+ self.new(i + io.read(8))
34
+ else
35
+ puts "Something: #{i}"
36
+ self.new(i)
37
+ end
38
+ end
39
+
40
+ def initialize(raw)
41
+ @head_raw = raw[0]
42
+ @head = @head_raw.unpack('C')[0]
43
+ @body = case @head
44
+ when 0xfd
45
+ @raw_i = raw[1..3]
46
+ @raw_i.unpack('v')[0]
47
+ when 0xfe
48
+ @raw_i = raw[1..5]
49
+ @raw_i.unpack('V')[0]
50
+ when 0xff
51
+ @raw_i = raw[1..9]
52
+ @raw_i.unpack('Q')[0]
53
+ else
54
+ @raw_i = @head_raw
55
+ @head
56
+ end
57
+ end
58
+
59
+ def to_i
60
+ @body
61
+ end
62
+
63
+ def head
64
+ @head
65
+ end
66
+
67
+ def body_raw
68
+ @raw_i
69
+ end
70
+
71
+ def body
72
+ @body
73
+ end
74
+
75
+ def raw
76
+ @head_raw + (@raw_i == @head_raw ? '' : @raw_i)
77
+ end
78
+
79
+ def method_missing(name, *args, &block)
80
+ ret = body.send(name, *args, &block)
81
+ ret
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,18 @@
1
+ require 'cryptocoin/protocol/var_len_int'
2
+
3
+ module Cryptocoin
4
+ module Protocol
5
+ class VarLenStr < String
6
+ def initialize(raw)
7
+ @size = Cryptocoin::Protocol::VarLenInt.new(raw)
8
+ @str = raw.read(@size)
9
+ end
10
+ def size
11
+ @size
12
+ end
13
+ def to_s
14
+ @str
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ require 'cryptocoin/protocol/message'
2
+ require 'cryptocoin/protocol/packet'
3
+ require 'cryptocoin/protocol/var_len_int'
4
+ require 'cryptocoin/protocol/var_len_str'
5
+
6
+ module Cryptocoin
7
+ module Protocol; end
8
+ end