monacoin-ruby 0.1.2 → 0.1.3

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.
@@ -0,0 +1,83 @@
1
+ require 'openssl'
2
+
3
+ module Litecoin
4
+ module Scrypt
5
+
6
+ def scrypt_1024_1_1_256_sp(input, scratchpad=[])
7
+ b = pbkdf2_sha256(input, input, 1, 128)
8
+ x = b.unpack("V*")
9
+ v = scratchpad
10
+
11
+ 1024.times{|i|
12
+ v[(i*32)...((i*32)+32)] = x.dup
13
+ xor_salsa8(x, x, 0, 16)
14
+ xor_salsa8(x, x, 16, 0)
15
+ }
16
+
17
+ 1024.times{|i|
18
+ j = 32 * (x[16] & 1023)
19
+ 32.times{|k| x[k] ^= v[j+k] }
20
+ xor_salsa8(x, x, 0, 16)
21
+ xor_salsa8(x, x, 16, 0)
22
+ }
23
+
24
+ pbkdf2_sha256(input, x.pack("V*"), 1, 32)
25
+ end
26
+
27
+ def pbkdf2_sha256(pass, salt, c=1, dk_len=128)
28
+ raise "pbkdf2_sha256: wrong length." if pass.bytesize != 80 or ![80,128].include?(salt.bytesize)
29
+ raise "pbkdf2_sha256: wrong dk length." if ![128,32].include?(dk_len)
30
+ OpenSSL::PKCS5.pbkdf2_hmac(pass, salt, iter=c, dk_len, OpenSSL::Digest::SHA256.new)
31
+ end
32
+
33
+ def rotl(a, b)
34
+ a &= 0xffffffff; ((a << b) | (a >> (32 - b))) & 0xffffffff
35
+ end
36
+
37
+ def xor_salsa8(a, b, a_offset, b_offset)
38
+ x = 16.times.map{|n| a[a_offset+n] ^= b[b_offset+n] }
39
+
40
+ 4.times{
41
+ [
42
+ [4, 0, 12, 7], [9, 5, 1, 7], [14, 10, 6, 7], [3, 15, 11, 7],
43
+ [8, 4, 0, 9], [13, 9, 5, 9], [2, 14, 10, 9], [7, 3, 15, 9],
44
+ [12, 8, 4, 13], [1, 13, 9, 13], [6, 2, 14, 13], [11, 7, 3, 13],
45
+ [0, 12, 8, 18], [5, 1, 13, 18], [10, 6, 2, 18], [15, 11, 7, 18],
46
+
47
+ [1, 0, 3, 7], [6, 5, 4, 7], [11, 10, 9, 7], [12, 15, 14, 7],
48
+ [2, 1, 0, 9], [7, 6, 5, 9], [8, 11, 10, 9], [13, 12, 15, 9],
49
+ [3, 2, 1, 13], [4, 7, 6, 13], [9, 8, 11, 13], [14, 13, 12, 13],
50
+ [0, 3, 2, 18], [5, 4, 7, 18], [10, 9, 8, 18], [15, 14, 13, 18]
51
+ ].each{|i|
52
+ x[ i[0] ] ^= rotl(x[ i[1] ] + x[ i[2] ], i[3])
53
+ }
54
+ }
55
+
56
+ 16.times{|n| a[a_offset+n] = (a[a_offset+n] + x[n]) & 0xffffffff }
57
+ true
58
+ end
59
+
60
+ extend self
61
+ end
62
+ end
63
+
64
+
65
+ if $0 == __FILE__
66
+ secret_hex = "020000004c1271c211717198227392b029a64a7971931d351b387bb80db027f270411e398a07046f7d4a08dd815412a8712f874a7ebf0507e3878bd24e20a3b73fd750a667d2f451eac7471b00de6659"
67
+ secret_bytes = [secret_hex].pack("H*")
68
+
69
+ begin
70
+ require "scrypt"
71
+ hash = SCrypt::Engine.__sc_crypt(secret_bytes, secret_bytes, 1024, 1, 1, 32)
72
+ p hash.reverse.unpack("H*")[0] == "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806"
73
+ rescue LoadError
74
+ puts "scrypt gem not found, using native scrypt"
75
+ p Litecoin::Scrypt.scrypt_1024_1_1_256_sp(secret_bytes).reverse.unpack("H*")[0] == "00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806"
76
+ end
77
+
78
+ require 'benchmark'
79
+ Benchmark.bmbm{|x|
80
+ x.report("v1"){ SCrypt::Engine.__sc_crypt(secret_bytes, secret_bytes, 1024, 1, 1, 32).reverse.unpack("H*") rescue nil }
81
+ x.report("v2"){ Litecoin::Scrypt.scrypt_1024_1_1_256_sp(secret_bytes).reverse.unpack("H*")[0] }
82
+ }
83
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: ascii-8bit
2
+
3
+ begin
4
+ require 'log4r'
5
+ # monkey-patch Log4r to accept level names as symbols
6
+ class Log4r::Logger
7
+ def level= l = 0
8
+ _level = l.is_a?(Fixnum) ? l : Log4r::LNAMES.index(l.to_s.upcase)
9
+ Log4r::Log4rTools.validate_level(_level)
10
+ @level = _level
11
+ LoggerFactory.define_methods(self)
12
+ Log4r::Logger.log_internal {"Logger '#{@fullname}' set to #{LNAMES[@level]}"}
13
+ @level
14
+ end
15
+ end
16
+ rescue LoadError
17
+ end
18
+
19
+ module Bitcoin
20
+ # this is a very simple logger that is used if log4r is not available
21
+ module Logger
22
+
23
+ module TimeLogger
24
+
25
+ def time message
26
+ time = Time.now
27
+ res = yield
28
+ debug { message % (Time.now - time) }
29
+ res
30
+ end
31
+
32
+ end
33
+
34
+ class Logger
35
+ LEVELS = [:debug, :info, :warn, :error, :fatal]
36
+
37
+ include TimeLogger
38
+
39
+ attr_accessor :level
40
+
41
+ def initialize(name)
42
+ @name, @level = name, :info
43
+ end
44
+
45
+ def level= level
46
+ @level = level.is_a?(Fixnum) ? LEVELS[level] : level.to_sym
47
+ end
48
+
49
+ LEVELS.each do |level|
50
+ define_method(level) do |*msg, &block|
51
+ return if LEVELS.index(level.to_sym) < LEVELS.index(@level.to_sym)
52
+ msg = block ? block.call : msg.join
53
+ puts "#{level.to_s.upcase.ljust(5)} #{@name}: #{msg}"
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ # wrap a logger and prepend a special name in front of the messages
60
+ class LogWrapper
61
+ def initialize(name, log); @name, @log = name, log; end
62
+ def method_missing(m, *a, &blk)
63
+ @log.send(m, *a, &proc{ "#{@name} #{blk.call}" })
64
+ end
65
+ end
66
+
67
+ # create a logger with given +name+. if log4r is installed, the logger
68
+ # will have a stdout and a fileout outputter to `log/<name>.log`.
69
+ # otherwise, the internal dummy logger is used which only logs to stdout.
70
+ def self.create name, level = :info
71
+ if defined?(Log4r)
72
+ dir = "log"
73
+ FileUtils.mkdir_p(dir) rescue dir = nil
74
+ @log = Log4r::Logger.new(name.to_s)
75
+ @log.extend(TimeLogger)
76
+ @log.level = level
77
+ @log.outputters << Log4r::Outputter.stdout
78
+ @log.outputters << Log4r::FileOutputter.new("fout", :filename => "#{dir}/#{name}.log") if dir
79
+ else
80
+ @log = Bitcoin::Logger::Logger.new(name)
81
+ end
82
+ @log
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,189 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'socket'
4
+ require 'digest/sha2'
5
+ require 'json'
6
+
7
+ module Bitcoin
8
+ module Protocol
9
+
10
+ # bitcoin/src/main.h
11
+ MAX_INV_SZ = 50000
12
+
13
+ # BIP 0031, pong message, is enabled for all versions AFTER this one
14
+ BIP0031_VERSION = 60000
15
+
16
+ autoload :ScriptWitness, 'bitcoin/protocol/script_witness'
17
+ autoload :TxIn, 'bitcoin/protocol/txin'
18
+ autoload :TxOut, 'bitcoin/protocol/txout'
19
+ autoload :Tx, 'bitcoin/protocol/tx'
20
+ autoload :Block, 'bitcoin/protocol/block'
21
+ autoload :Addr, 'bitcoin/protocol/address'
22
+ autoload :Alert, 'bitcoin/protocol/alert'
23
+ autoload :Reject, 'bitcoin/protocol/reject'
24
+ autoload :Version, 'bitcoin/protocol/version'
25
+ autoload :AuxPow, 'bitcoin/protocol/aux_pow'
26
+ autoload :PartialMerkleTree, 'bitcoin/protocol/partial_merkle_tree'
27
+
28
+ autoload :Handler, 'bitcoin/protocol/handler'
29
+ autoload :Parser, 'bitcoin/protocol/parser'
30
+
31
+ Uniq = rand(0xffffffffffffffff)
32
+
33
+ # var_int refers to https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer and is what Satoshi called "CompactSize"
34
+ # BitcoinQT has later added even more compact format called CVarInt to use in its local block storage. CVarInt is not implemented here.
35
+ def self.unpack_var_int(payload)
36
+ case payload.unpack("C")[0] # TODO add test cases
37
+ when 0xfd; payload.unpack("xva*")
38
+ when 0xfe; payload.unpack("xVa*")
39
+ when 0xff; payload.unpack("xQa*") # TODO add little-endian version of Q
40
+ else; payload.unpack("Ca*")
41
+ end
42
+ end
43
+
44
+ def self.unpack_var_int_from_io(io)
45
+ uchar = io.read(1).unpack("C")[0]
46
+ case uchar
47
+ when 0xfd; io.read(2).unpack("v")[0]
48
+ when 0xfe; io.read(4).unpack("V")[0]
49
+ when 0xff; io.read(8).unpack("Q")[0]
50
+ else; uchar
51
+ end
52
+ end
53
+
54
+ def self.pack_var_int(i)
55
+ if i < 0xfd; [ i].pack("C")
56
+ elsif i <= 0xffff; [0xfd, i].pack("Cv")
57
+ elsif i <= 0xffffffff; [0xfe, i].pack("CV")
58
+ elsif i <= 0xffffffffffffffff; [0xff, i].pack("CQ")
59
+ else raise "int(#{i}) too large!"
60
+ end
61
+ end
62
+
63
+ def self.unpack_var_string(payload)
64
+ size, payload = unpack_var_int(payload)
65
+ size > 0 ? (string, payload = payload.unpack("a#{size}a*")) : [nil, payload]
66
+ end
67
+
68
+ def self.unpack_var_string_from_io(buf)
69
+ size = unpack_var_int_from_io(buf)
70
+ size > 0 ? buf.read(size) : nil
71
+ end
72
+
73
+ def self.pack_var_string(payload)
74
+ pack_var_int(payload.bytesize) + payload
75
+ end
76
+
77
+ def self.unpack_var_string_array(payload) # unpacks set<string>
78
+ buf = StringIO.new(payload)
79
+ size = unpack_var_int_from_io(buf)
80
+ return [nil, buf.read] if size == 0
81
+ strings = []
82
+ size.times{
83
+ break if buf.eof?
84
+ strings << unpack_var_string_from_io(buf)
85
+ }
86
+ [strings, buf.read]
87
+ end
88
+
89
+ def self.unpack_var_int_array(payload) # unpacks set<int>
90
+ buf = StringIO.new(payload)
91
+ size = unpack_var_int_from_io(buf)
92
+ return [nil, buf.read] if size == 0
93
+ ints = []
94
+ size.times{
95
+ break if buf.eof?
96
+ ints << unpack_var_int_from_io(buf)
97
+ }
98
+ [ints, buf.read]
99
+ end
100
+
101
+ def self.unpack_boolean(payload)
102
+ bdata, payload = payload.unpack("Ca*")
103
+ [ (bdata == 0 ? false : true), payload ]
104
+ end
105
+
106
+ def self.pack_boolean(b)
107
+ (b == true) ? [0xFF].pack("C") : [0x00].pack("C")
108
+ end
109
+
110
+ BINARY = Encoding.find('ASCII-8BIT')
111
+
112
+ def self.pkt(command, payload)
113
+ cmd = command.ljust(12, "\x00")[0...12]
114
+ length = [payload.bytesize].pack("V")
115
+ checksum = Digest::SHA256.digest(Digest::SHA256.digest(payload))[0...4]
116
+ pkt = "".force_encoding(BINARY)
117
+ pkt << Bitcoin.network[:magic_head].force_encoding(BINARY) << cmd.force_encoding(BINARY) << length << checksum << payload.force_encoding(BINARY)
118
+ end
119
+
120
+ def self.version_pkt(from_id, from=nil, to=nil, last_block=nil, time=nil, user_agent=nil, version=nil)
121
+ opts = if from_id.is_a?(Hash)
122
+ from_id
123
+ else
124
+ STDERR.puts "Bitcoin::Protocol.version_pkt - API deprecated. please change it soon.."
125
+ {
126
+ :nonce => from_id, :from => from, :to => to, :last_block => last_block,
127
+ :time => time, :user_agent => user_agent, :version => version
128
+ }
129
+ end
130
+ version = Protocol::Version.new(opts)
131
+ version.to_pkt
132
+ end
133
+
134
+ def self.ping_pkt(nonce = rand(0xffffffff))
135
+ pkt("ping", [nonce].pack("Q"))
136
+ end
137
+
138
+ def self.pong_pkt(nonce)
139
+ pkt("pong", [nonce].pack("Q"))
140
+ end
141
+
142
+ def self.verack_pkt
143
+ pkt("verack", "")
144
+ end
145
+
146
+ TypeLookup = Hash[:tx, 1, :block, 2, :filtered_block, 3, nil, 0]
147
+
148
+ def self.getdata_pkt(type, hashes)
149
+ return if hashes.size > MAX_INV_SZ
150
+ t = [ TypeLookup[type] ].pack("V")
151
+ pkt("getdata", pack_var_int(hashes.size) + hashes.map{|hash| t + hash[0..32].reverse }.join)
152
+ end
153
+
154
+ def self.inv_pkt(type, hashes)
155
+ return if hashes.size > MAX_INV_SZ
156
+ t = [ TypeLookup[type] ].pack("V")
157
+ pkt("inv", pack_var_int(hashes.size) + hashes.map{|hash| t + hash[0..32].reverse }.join)
158
+ end
159
+
160
+ DEFAULT_STOP_HASH = "00"*32
161
+
162
+ def self.locator_payload(version, locator_hashes, stop_hash)
163
+ [
164
+ [version].pack("V"),
165
+ pack_var_int(locator_hashes.size),
166
+ locator_hashes.map{|l| l.htb_reverse }.join,
167
+ stop_hash.htb_reverse
168
+ ].join
169
+ end
170
+
171
+ def self.getblocks_pkt(version, locator_hashes, stop_hash=DEFAULT_STOP_HASH)
172
+ pkt "getblocks", locator_payload(version, locator_hashes, stop_hash)
173
+ end
174
+
175
+ def self.getheaders_pkt(version, locator_hashes, stop_hash=DEFAULT_STOP_HASH)
176
+ pkt "getheaders", locator_payload(version, locator_hashes, stop_hash)
177
+ end
178
+
179
+ def self.headers_pkt(version, blocks)
180
+ pkt "headers", [pack_var_int(blocks.size), blocks.map{|block| block.block_header}.join].join
181
+ end
182
+
183
+ def self.read_binary_file(path)
184
+ File.open(path, 'rb'){|f| f.read }
185
+ end
186
+ end
187
+
188
+ P = Protocol
189
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ class Addr < Struct.new(:time, :service, :ip, :port)
7
+
8
+ # # IP Address / Port
9
+ # attr_reader :ip, :port
10
+
11
+ # # Time the node was last active
12
+ # attr_reader :time
13
+
14
+ # # Services supported by this node
15
+ # attr_reader :service
16
+
17
+ # create addr from raw binary +data+
18
+ def initialize(data = nil)
19
+ if data
20
+ self[:time], self[:service], self[:ip], self[:port] = data.unpack("VQx12a4n")
21
+ self[:ip] = ip.unpack("C*").join(".")
22
+ else
23
+ self[:time], self[:service] = Time.now.to_i, 1
24
+ self[:ip], self[:port] = "127.0.0.1", Bitcoin.network[:default_port]
25
+ end
26
+ end
27
+
28
+ # is this address alive?
29
+ def alive?
30
+ (Time.now.tv_sec-7200) <= self[:time]
31
+ end
32
+
33
+ def to_payload
34
+ ip = self[:ip].split(".").map(&:to_i)
35
+ [ time, service, ("\x00"*10)+"\xff\xff", *ip, port ].pack("VQa12C4n")
36
+ end
37
+
38
+ def string
39
+ "#{self[:ip]}:#{self[:port]}"
40
+ end
41
+
42
+ def self.pkt(*addrs)
43
+ addrs = addrs.select{|i| i.is_a?(Bitcoin::Protocol::Addr) && i.ip =~ /^\d+\.\d+\.\d+\.\d+$/ }
44
+ length = Bitcoin::Protocol.pack_var_int(addrs.size)
45
+ Bitcoin::Protocol.pkt("addr", length + addrs.map(&:to_payload).join)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ class Alert < Struct.new(:version, :relay_until, :expiration, :id, :cancel, :set_cancel,
7
+ :min_ver, :max_ver, :set_sub_ver, :priority, :comment, :status_bar, :reserved)
8
+
9
+ attr_accessor :payload, :signature
10
+
11
+ def initialize(values, alert_payload=nil, alert_signature=nil)
12
+ @payload, @signature = alert_payload, alert_signature
13
+ super(*values)
14
+ end
15
+
16
+ def valid_signature?
17
+ return false unless @payload && @signature
18
+ hash = Digest::SHA256.digest(Digest::SHA256.digest(@payload))
19
+ Bitcoin.network[:alert_pubkeys].any?{|public_key| Bitcoin.verify_signature(hash, @signature, public_key) }
20
+ end
21
+
22
+
23
+ def self.parse(payload)
24
+ count, payload = Bitcoin::Protocol.unpack_var_int(payload)
25
+ alert_payload, payload = payload.unpack("a#{count}a*")
26
+ count, payload = Bitcoin::Protocol.unpack_var_int(payload)
27
+ alert_signature, payload = payload.unpack("a#{count}a*")
28
+
29
+ version, relay_until, expiration, id, cancel, payload = alert_payload.unpack("VQQVVa*")
30
+
31
+ set_cancel, payload = Bitcoin::Protocol.unpack_var_int_array(payload)
32
+ min_ver, max_ver, payload = payload.unpack("VVa*")
33
+ set_sub_ver, payload = Bitcoin::Protocol.unpack_var_string_array(payload)
34
+ priority, payload = payload.unpack("Va*")
35
+ comment, payload = Bitcoin::Protocol.unpack_var_string(payload)
36
+ status_bar, payload = Bitcoin::Protocol.unpack_var_string(payload)
37
+ reserved, payload = Bitcoin::Protocol.unpack_var_string(payload)
38
+
39
+ values = [ version, relay_until, expiration, id, cancel, set_cancel, min_ver, max_ver, set_sub_ver, priority, comment, status_bar, reserved ]
40
+
41
+ new(values, alert_payload, alert_signature)
42
+ end
43
+ end
44
+
45
+ end
46
+ end