cryptocoin 0.0.1b

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