monacoin-ruby 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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