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