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.
- checksums.yaml +4 -4
- data/lib/bitcoin.rb +933 -0
- data/lib/bitcoin/bloom_filter.rb +125 -0
- data/lib/bitcoin/builder.rb +494 -0
- data/lib/bitcoin/connection.rb +130 -0
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ext_key.rb +191 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +75 -0
- data/lib/bitcoin/ffi/openssl.rb +388 -0
- data/lib/bitcoin/ffi/secp256k1.rb +266 -0
- data/lib/bitcoin/key.rb +280 -0
- data/lib/bitcoin/litecoin.rb +83 -0
- data/lib/bitcoin/logger.rb +86 -0
- data/lib/bitcoin/protocol.rb +189 -0
- data/lib/bitcoin/protocol/address.rb +50 -0
- data/lib/bitcoin/protocol/alert.rb +46 -0
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +285 -0
- data/lib/bitcoin/protocol/handler.rb +43 -0
- data/lib/bitcoin/protocol/parser.rb +194 -0
- data/lib/bitcoin/protocol/partial_merkle_tree.rb +61 -0
- data/lib/bitcoin/protocol/reject.rb +38 -0
- data/lib/bitcoin/protocol/script_witness.rb +31 -0
- data/lib/bitcoin/protocol/tx.rb +587 -0
- data/lib/bitcoin/protocol/txin.rb +142 -0
- data/lib/bitcoin/protocol/txout.rb +95 -0
- data/lib/bitcoin/protocol/version.rb +88 -0
- data/lib/bitcoin/script.rb +1656 -0
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +3 -0
- metadata +32 -1
@@ -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
|