bitcoinrb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +4 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/bitcoinrb.gemspec +32 -0
  15. data/exe/bitcoinrb-cli +5 -0
  16. data/exe/bitcoinrbd +49 -0
  17. data/lib/bitcoin.rb +121 -0
  18. data/lib/bitcoin/base58.rb +40 -0
  19. data/lib/bitcoin/block_header.rb +41 -0
  20. data/lib/bitcoin/chain_params.rb +57 -0
  21. data/lib/bitcoin/chainparams/mainnet.yml +25 -0
  22. data/lib/bitcoin/chainparams/regtest.yml +20 -0
  23. data/lib/bitcoin/chainparams/testnet.yml +24 -0
  24. data/lib/bitcoin/connection.rb +66 -0
  25. data/lib/bitcoin/ext_key.rb +205 -0
  26. data/lib/bitcoin/key.rb +131 -0
  27. data/lib/bitcoin/logger.rb +18 -0
  28. data/lib/bitcoin/merkle_tree.rb +120 -0
  29. data/lib/bitcoin/message.rb +42 -0
  30. data/lib/bitcoin/message/addr.rb +74 -0
  31. data/lib/bitcoin/message/base.rb +40 -0
  32. data/lib/bitcoin/message/block.rb +41 -0
  33. data/lib/bitcoin/message/error.rb +10 -0
  34. data/lib/bitcoin/message/fee_filter.rb +27 -0
  35. data/lib/bitcoin/message/filter_add.rb +28 -0
  36. data/lib/bitcoin/message/filter_clear.rb +17 -0
  37. data/lib/bitcoin/message/filter_load.rb +43 -0
  38. data/lib/bitcoin/message/get_addr.rb +17 -0
  39. data/lib/bitcoin/message/get_blocks.rb +29 -0
  40. data/lib/bitcoin/message/get_data.rb +21 -0
  41. data/lib/bitcoin/message/get_headers.rb +28 -0
  42. data/lib/bitcoin/message/handler.rb +170 -0
  43. data/lib/bitcoin/message/headers.rb +34 -0
  44. data/lib/bitcoin/message/headers_parser.rb +24 -0
  45. data/lib/bitcoin/message/inv.rb +21 -0
  46. data/lib/bitcoin/message/inventories_parser.rb +23 -0
  47. data/lib/bitcoin/message/inventory.rb +47 -0
  48. data/lib/bitcoin/message/mem_pool.rb +17 -0
  49. data/lib/bitcoin/message/merkle_block.rb +42 -0
  50. data/lib/bitcoin/message/not_found.rb +29 -0
  51. data/lib/bitcoin/message/ping.rb +30 -0
  52. data/lib/bitcoin/message/pong.rb +26 -0
  53. data/lib/bitcoin/message/reject.rb +46 -0
  54. data/lib/bitcoin/message/send_cmpct.rb +43 -0
  55. data/lib/bitcoin/message/send_headers.rb +16 -0
  56. data/lib/bitcoin/message/tx.rb +30 -0
  57. data/lib/bitcoin/message/ver_ack.rb +17 -0
  58. data/lib/bitcoin/message/version.rb +79 -0
  59. data/lib/bitcoin/mnemonic.rb +76 -0
  60. data/lib/bitcoin/mnemonic/wordlist/chinese_simplified.txt +2048 -0
  61. data/lib/bitcoin/mnemonic/wordlist/chinese_traditional.txt +2048 -0
  62. data/lib/bitcoin/mnemonic/wordlist/english.txt +2048 -0
  63. data/lib/bitcoin/mnemonic/wordlist/french.txt +2048 -0
  64. data/lib/bitcoin/mnemonic/wordlist/italian.txt +2048 -0
  65. data/lib/bitcoin/mnemonic/wordlist/japanese.txt +2048 -0
  66. data/lib/bitcoin/mnemonic/wordlist/spanish.txt +2048 -0
  67. data/lib/bitcoin/nodes.rb +5 -0
  68. data/lib/bitcoin/nodes/spv.rb +13 -0
  69. data/lib/bitcoin/nodes/spv/cli.rb +12 -0
  70. data/lib/bitcoin/nodes/spv/daemon.rb +21 -0
  71. data/lib/bitcoin/opcodes.rb +172 -0
  72. data/lib/bitcoin/out_point.rb +31 -0
  73. data/lib/bitcoin/script/script.rb +347 -0
  74. data/lib/bitcoin/script/script_error.rb +168 -0
  75. data/lib/bitcoin/script/script_interpreter.rb +694 -0
  76. data/lib/bitcoin/script/tx_checker.rb +44 -0
  77. data/lib/bitcoin/script_witness.rb +29 -0
  78. data/lib/bitcoin/secp256k1.rb +10 -0
  79. data/lib/bitcoin/secp256k1/native.rb +22 -0
  80. data/lib/bitcoin/secp256k1/ruby.rb +96 -0
  81. data/lib/bitcoin/tx.rb +191 -0
  82. data/lib/bitcoin/tx_in.rb +45 -0
  83. data/lib/bitcoin/tx_out.rb +32 -0
  84. data/lib/bitcoin/util.rb +105 -0
  85. data/lib/bitcoin/version.rb +3 -0
  86. metadata +256 -0
@@ -0,0 +1,5 @@
1
+ module Bitcoin
2
+ module Nodes
3
+ autoload :SPV, 'bitcoin/nodes/spv'
4
+ end
5
+ end
@@ -0,0 +1,13 @@
1
+ module Bitcoin
2
+ module Nodes
3
+
4
+ # SPV module
5
+ module SPV
6
+
7
+ autoload :CLI, 'bitcoin/nodes/spv/cli'
8
+ autoload :Daemon, 'bitcoin/nodes/spv/daemon'
9
+
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ require 'thor'
2
+
3
+ module Bitcoin
4
+ module Nodes
5
+ module SPV
6
+
7
+ class CLI < Thor
8
+
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ require 'daemon_spawn'
2
+ module Bitcoin
3
+ module Nodes
4
+ module SPV
5
+
6
+ # SPV node daemon
7
+ class Daemon < DaemonSpawn::Base
8
+
9
+ def start(args)
10
+
11
+ end
12
+
13
+ def stop
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,172 @@
1
+ module Bitcoin
2
+
3
+ # https://bitcoin.org/en/developer-reference#opcodes
4
+ module Opcodes
5
+
6
+ module_function
7
+
8
+ # https://en.bitcoin.it/wiki/Script#Constants
9
+ OP_0 = 0x00
10
+ OP_1 = 0x51
11
+ OP_2 = 0x52
12
+ OP_3 = 0x53
13
+ OP_4 = 0x54
14
+ OP_5 = 0x55
15
+ OP_6 = 0x56
16
+ OP_7 = 0x57
17
+ OP_8 = 0x58
18
+ OP_9 = 0x59
19
+ OP_10 = 0x5a
20
+ OP_11 = 0x5b
21
+ OP_12 = 0x5c
22
+ OP_13 = 0x5d
23
+ OP_14 = 0x5e
24
+ OP_15 = 0x5f
25
+ OP_16 = 0x60
26
+
27
+ OP_PUSHDATA1 = 0x4c
28
+ OP_PUSHDATA2 = 0x4d
29
+ OP_PUSHDATA4 = 0x4e
30
+ OP_1NEGATE = 0x4f
31
+
32
+ # https://en.bitcoin.it/wiki/Script#Flow_control
33
+ OP_NOP = 0x61
34
+ OP_IF = 0x63
35
+ OP_NOTIF = 0x64
36
+ OP_ELSE = 0x67
37
+ OP_ENDIF = 0x68
38
+ OP_VERIFY = 0x69
39
+ OP_RETURN = 0x6a
40
+
41
+ # https://en.bitcoin.it/wiki/Script#Stack
42
+ OP_TOALTSTACK = 0x6b
43
+ OP_FROMALTSTACK = 0x6c
44
+ OP_IFDUP = 0x73
45
+ OP_DEPTH = 0x74
46
+ OP_DROP = 0x75
47
+ OP_DUP = 0x76
48
+ OP_NIP = 0x77
49
+ OP_OVER = 0x78
50
+ OP_PICK = 0x79
51
+ OP_ROLL = 0x7a
52
+ OP_ROT = 0x7b
53
+ OP_SWAP = 0x7c
54
+ OP_TUCK = 0x7d
55
+ OP_2DROP = 0x6d
56
+ OP_2DUP = 0x6e
57
+ OP_3DUP = 0x6f
58
+ OP_2OVER = 0x70
59
+ OP_2ROT = 0x71
60
+ OP_2SWAP = 0x72
61
+
62
+ # https://en.bitcoin.it/wiki/Script#Splice
63
+ OP_CAT = 0x7e # disabled
64
+ OP_SUBSTR = 0x7f # disabled
65
+ OP_LEFT = 0x80 # disabled
66
+ OP_RIGHT = 0x81 # disabled
67
+ OP_SIZE = 0x82
68
+
69
+ # https://en.bitcoin.it/wiki/Script#Bitwise_logic
70
+ OP_INVERT = 0x83 # disabled
71
+ OP_AND = 0x84 # disabled
72
+ OP_OR = 0x85 # disabled
73
+ OP_XOR = 0x86 # disabled
74
+ OP_EQUAL = 0x87
75
+ OP_EQUALVERIFY = 0x88
76
+
77
+ # https://en.bitcoin.it/wiki/Script#Arithmetic
78
+ OP_1ADD = 0x8b
79
+ OP_1SUB = 0x8c
80
+ OP_2MUL = 0x8d # disabled
81
+ OP_2DIV = 0x8e # disabled
82
+ OP_NEGATE = 0x8f
83
+ OP_ABS = 0x90
84
+ OP_NOT = 0x91
85
+ OP_0NOTEQUAL = 0x92
86
+ OP_ADD = 0x93
87
+ OP_SUB = 0x94
88
+ OP_MUL = 0x95 # disabled
89
+ OP_DIV = 0x96 # disabled
90
+ OP_MOD = 0x97 # disabled
91
+ OP_LSHIFT = 0x98 # disabled
92
+ OP_RSHIFT = 0x99 # disabled
93
+ OP_BOOLAND = 0x9a
94
+ OP_BOOLOR = 0x9b
95
+ OP_NUMEQUAL = 0x9c
96
+ OP_NUMEQUALVERIFY = 0x9d
97
+ OP_NUMNOTEQUAL = 0x9e
98
+ OP_LESSTHAN = 0x9f
99
+ OP_GREATERTHAN = 0xa0
100
+ OP_LESSTHANOREQUAL = 0xa1
101
+ OP_GREATERTHANOREQUAL = 0xa2
102
+ OP_MIN = 0xa3
103
+ OP_MAX = 0xa4
104
+ OP_WITHIN = 0xa5
105
+
106
+ # https://en.bitcoin.it/wiki/Script#Crypto
107
+ OP_RIPEMD160 = 0xa6
108
+ OP_SHA1 = 0xa7
109
+ OP_SHA256 = 0xa8
110
+ OP_HASH160 = 0xa9
111
+ OP_HASH256 = 0xaa
112
+ OP_CODESEPARATOR = 0xab
113
+ OP_CHECKSIG = 0xac
114
+ OP_CHECKSIGVERIFY= 0xad
115
+ OP_CHECKMULTISIG = 0xae
116
+ OP_CHECKMULTISIGVERIFY = 0xaf
117
+
118
+ # https://en.bitcoin.it/wiki/Script#Locktime
119
+ OP_CHECKLOCKTIMEVERIFY = OP_NOP2 = 0xb1
120
+ OP_CHECKSEQUENCEVERIFY = OP_NOP3 = 0xb2
121
+
122
+ # https://en.bitcoin.it/wiki/Script#Reserved_words
123
+ OP_RESERVED = 0x50
124
+ OP_VER = 0x62
125
+ OP_VERIF = 0x65
126
+ OP_VERNOTIF = 0x66
127
+ OP_RESERVED1= 0x89
128
+ OP_RESERVED2 = 0x8a
129
+
130
+ OP_NOP1 = 0xb0
131
+ OP_NOP4 = 0xb3
132
+ OP_NOP5 = 0xb4
133
+ OP_NOP6 = 0xb5
134
+ OP_NOP7 = 0xb6
135
+ OP_NOP8 = 0xb7
136
+ OP_NOP9 = 0xb8
137
+ OP_NOP10 = 0xb9
138
+
139
+ OPCODES_MAP = Hash[*constants.grep(/^OP_/).map { |c| [const_get(c), c.to_s] }.flatten]
140
+ NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
141
+
142
+ def opcode_to_name(opcode)
143
+ return OPCODES_MAP[opcode].delete('OP_') if opcode == OP_0 || (opcode <= OP_16 && opcode >= OP_1)
144
+ OPCODES_MAP[opcode]
145
+ end
146
+
147
+ def name_to_opcode(name)
148
+ return NAME_MAP['OP_' + name] if name =~ /^\d/ && name.to_i < 17 && name.to_i > -1
149
+ NAME_MAP[name]
150
+ end
151
+
152
+ # whether opcode is predefined opcode
153
+ def defined?(opcode)
154
+ !opcode_to_name(opcode).nil?
155
+ end
156
+
157
+ def small_int_to_opcode(int)
158
+ return OP_0 if int == 0
159
+ return OP_1NEGATE if int == -1
160
+ return OP_1 + (int - 1) if int >= 1 && int <= 16
161
+ nil
162
+ end
163
+
164
+ def opcode_to_small_int(opcode)
165
+ return 0 if opcode == ''.b || opcode == OP_0
166
+ return -1 if opcode == OP_1NEGATE
167
+ return opcode - (OP_1 - 1) if opcode >= OP_1 && opcode <= OP_16
168
+ nil
169
+ end
170
+
171
+ end
172
+ end
@@ -0,0 +1,31 @@
1
+ module Bitcoin
2
+
3
+ # outpoint class
4
+ class OutPoint
5
+
6
+ COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
7
+ COINBASE_INDEX = 4294967295
8
+
9
+ attr_reader :hash
10
+ attr_reader :index
11
+
12
+ def initialize(hash, index)
13
+ @hash = hash
14
+ @index = index
15
+ end
16
+
17
+ def coinbase?
18
+ hash == COINBASE_HASH && index == COINBASE_INDEX
19
+ end
20
+
21
+ def to_payload
22
+ [hash.htb.reverse, index].pack('a32V')
23
+ end
24
+
25
+ def self.create_coinbase_outpoint
26
+ new(COINBASE_HASH, COINBASE_INDEX)
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,347 @@
1
+ module Bitcoin
2
+
3
+ # bitcoin script
4
+ class Script
5
+ include Bitcoin::Opcodes
6
+
7
+ # witness version
8
+ WITNESS_VERSION = 0x00
9
+
10
+ # Maximum script length in bytes
11
+ MAX_SCRIPT_SIZE = 10000
12
+
13
+ # Maximum number of public keys per multisig
14
+ MAX_PUBKEYS_PER_MULTISIG = 20
15
+
16
+ # Maximum number of non-push operations per script
17
+ MAX_OPS_PER_SCRIPT = 201
18
+
19
+ # Maximum number of bytes pushable to the stack
20
+ MAX_SCRIPT_ELEMENT_SIZE = 520
21
+
22
+ # Maximum number of size in the stack
23
+ MAX_STACK_SIZE = 1000
24
+
25
+ # Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
26
+ LOCKTIME_THRESHOLD = 500000000
27
+
28
+ # Signature hash types/flags
29
+ SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
30
+
31
+ # Maximum number length in bytes
32
+ DEFAULT_MAX_NUM_SIZE = 4
33
+
34
+ attr_accessor :chunks
35
+
36
+ def initialize
37
+ @chunks = []
38
+ end
39
+
40
+ # generate P2PKH script
41
+ def self.to_p2pkh(pubkey_hash)
42
+ new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
43
+ end
44
+
45
+ # generate P2WPKH script
46
+ def self.to_p2wpkh(pubkey_hash)
47
+ new << WITNESS_VERSION << pubkey_hash
48
+ end
49
+
50
+ # generate m of n multisig p2sh script
51
+ # @param [String] m the number of signatures required for multisig
52
+ # @param [Array] pubkeys array of public keys that compose multisig
53
+ # @return [Script, Script] first element is p2sh script, second one is redeem script.
54
+ def self.to_p2sh_multisig_script(m, pubkeys)
55
+ redeem_script = to_multisig_script(m, pubkeys)
56
+ p2sh_script = new << OP_HASH160 << redeem_script.to_hash160 << OP_EQUAL
57
+ [p2sh_script, redeem_script]
58
+ end
59
+
60
+ # generate m of n multisig script
61
+ # @param [String] m the number of signatures required for multisig
62
+ # @param [Array] pubkeys array of public keys that compose multisig
63
+ # @return [Script] multisig script.
64
+ def self.to_multisig_script(m, pubkeys)
65
+ new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
66
+ end
67
+
68
+ # generate p2wsh script for +redeem_script+
69
+ # @param [Script] redeem_script target redeem script
70
+ # @param [Script] p2wsh script
71
+ def self.to_p2wsh(redeem_script)
72
+ new << WITNESS_VERSION << redeem_script.to_sha256
73
+ end
74
+
75
+ # generate script from string.
76
+ def self.from_string(string)
77
+ script = new
78
+ string.split(' ').each do |v|
79
+ opcode = Opcodes.name_to_opcode(v)
80
+ if opcode
81
+ script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
82
+ else
83
+ script << v
84
+ end
85
+ end
86
+ script
87
+ end
88
+
89
+ def self.parse_from_payload(payload)
90
+ s = new
91
+ buf = StringIO.new(payload)
92
+ until buf.eof?
93
+ opcode = buf.read(1)
94
+ if opcode.pushdata?
95
+ pushcode = opcode.ord
96
+ len = case pushcode
97
+ when OP_PUSHDATA1
98
+ buf.read(1).unpack('C').first
99
+ when OP_PUSHDATA2
100
+ buf.read(2).unpack('v').first
101
+ when OP_PUSHDATA4
102
+ buf.read(4).unpack('V').first
103
+ else
104
+ pushcode if pushcode < OP_PUSHDATA1
105
+ end
106
+ s << buf.read(len).bth if len
107
+ else
108
+ s << opcode.ord
109
+ end
110
+ end
111
+ s
112
+ end
113
+
114
+ def to_payload
115
+ chunks.join
116
+ end
117
+
118
+ def to_addr
119
+ return p2pkh_addr if p2pkh?
120
+ return p2wpkh_addr if p2wpkh?
121
+ return p2wsh_addr if p2wsh?
122
+ return p2sh_addr if p2sh?
123
+ end
124
+
125
+ # whether this script is a P2PKH format script.
126
+ def p2pkh?
127
+ return false unless chunks.size == 5
128
+ [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
129
+ (chunks[0..1]+ chunks[3..4]).map(&:ord) && chunks[2].bytesize == 21
130
+ end
131
+
132
+ # whether this script is a P2WPKH format script.
133
+ def p2wpkh?
134
+ return false unless chunks.size == 2
135
+ chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 21
136
+ end
137
+
138
+ def p2wsh?
139
+ return false unless chunks.size == 2
140
+ chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 33
141
+ end
142
+
143
+ def p2sh?
144
+ return false unless chunks.size == 3
145
+ OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
146
+ end
147
+
148
+ # whether data push only script which dose not include other opcode
149
+ def data_only?
150
+ chunks.each do |c|
151
+ return false if !c.opcode.nil? && c.opcode > OP_16
152
+ end
153
+ true
154
+ end
155
+
156
+ # A witness program is any valid Script that consists of a 1-byte push opcode followed by a data push between 2 and 40 bytes.
157
+ def witness_program?
158
+ return false if size < 4 || size > 42 || chunks.size < 2
159
+ opcode = chunks[0].opcode
160
+ return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
161
+ return false unless chunks[1].pushdata?
162
+ program_size = chunks[1].pushed_data.bytesize
163
+ program_size >= 2 && program_size <= 40
164
+ end
165
+
166
+ # get witness version and witness program
167
+ def witness_data
168
+ version = opcode_to_small_int(chunks[0].opcode)
169
+ program = chunks[1].pushed_data
170
+ [version, program]
171
+ end
172
+
173
+ # append object to payload
174
+ def <<(obj)
175
+ if obj.is_a?(Integer)
176
+ push_int(obj)
177
+ elsif obj.is_a?(String)
178
+ append_data(obj.b)
179
+ elsif obj.is_a?(Array)
180
+ obj.each { |o| self.<< o}
181
+ self
182
+ end
183
+ end
184
+
185
+ # push integer to stack.
186
+ def push_int(n)
187
+ begin
188
+ append_opcode(n)
189
+ rescue ArgumentError
190
+ append_data(Script.encode_number(n))
191
+ end
192
+ self
193
+ end
194
+
195
+ # append opcode to payload
196
+ # @param [Integer] opcode append opcode which defined by Bitcoin::Opcodes
197
+ # @return [Script] return self
198
+ def append_opcode(opcode)
199
+ opcode = Opcodes.small_int_to_opcode(opcode) if -1 <= opcode && opcode <= 16
200
+ raise ArgumentError, "specified invalid opcode #{opcode}." unless Opcodes.defined?(opcode)
201
+ chunks << opcode.chr
202
+ self
203
+ end
204
+
205
+ # append data to payload with pushdata opcode
206
+ # @param [String] data append data. this data is not binary
207
+ # @return [Script] return self
208
+ def append_data(data)
209
+ chunks << Bitcoin::Script.pack_pushdata(data.htb)
210
+ self
211
+ end
212
+
213
+ def to_s
214
+ chunks.map { |c|
215
+ if c.pushdata?
216
+ v = Opcodes.opcode_to_small_int(c.ord)
217
+ v ? v : c.pushed_data.bth
218
+ else
219
+ Opcodes.opcode_to_name(c.ord)
220
+ end
221
+ }.join(' ')
222
+ end
223
+
224
+ # generate sha-256 hash for payload
225
+ def to_sha256
226
+ Bitcoin.sha256(to_payload).bth
227
+ end
228
+
229
+ # generate hash160 hash for payload
230
+ def to_hash160
231
+ Bitcoin.hash160(to_payload.bth)
232
+ end
233
+
234
+ # script size
235
+ def size
236
+ to_payload.bytesize
237
+ end
238
+
239
+ # encode int value to script number hex.
240
+ # The stacks hold byte vectors.
241
+ # When used as numbers, byte vectors are interpreted as little-endian variable-length integers
242
+ # with the most significant bit determining the sign of the integer.
243
+ # Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0).
244
+ # Positive 0 is represented by a null-length vector.
245
+ # Byte vectors are interpreted as Booleans where False is represented by any representation of zero,
246
+ # and True is represented by any representation of non-zero.
247
+ def self.encode_number(i)
248
+ return '' if i == 0
249
+ negative = i < 0
250
+
251
+ hex = i.abs.to_s(16)
252
+ hex = '0' + hex unless (hex.length % 2).zero?
253
+ v = hex.htb.reverse # change endian
254
+
255
+ v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack('C').first & 0x80) == 0
256
+ v[-1] = [v[-1].unpack('C').first | 0x80].pack('C') if negative
257
+ v.bth
258
+ end
259
+
260
+ # decode script number hex to int value
261
+ def self.decode_number(s)
262
+ v = s.htb.reverse
263
+ return 0 if v.length.zero?
264
+ mbs = v[0].unpack('C').first
265
+ v[0] = [mbs - 0x80].pack('C') unless (mbs & 0x80) == 0
266
+ result = v.bth.to_i(16)
267
+ result = -result unless (mbs & 0x80) == 0
268
+ result
269
+ end
270
+
271
+ # binary +data+ convert pushdata which contains data length and append PUSHDATA opcode if necessary.
272
+ def self.pack_pushdata(data)
273
+ size = data.bytesize
274
+ header = if size < OP_PUSHDATA1
275
+ [size].pack('C')
276
+ elsif size < 0xff
277
+ [OP_PUSHDATA1, size].pack('CC')
278
+ elsif size < 0xffff
279
+ [OP_PUSHDATA2, size].pack('Cv')
280
+ elsif size < 0xffffffff
281
+ [OP_PUSHDATA4, size].pack('CV')
282
+ else
283
+ raise ArgumentError, 'data size is too big.'
284
+ end
285
+ header + data
286
+ end
287
+
288
+ # subscript this script to the specified range.
289
+ def subscript(*args)
290
+ s = self.class.new
291
+ s.chunks = chunks[*args]
292
+ s
293
+ end
294
+
295
+ # removes chunks matching subscript byte-for-byte and returns as a new object.
296
+ def find_and_delete(subscript)
297
+ raise ArgumentError, 'subscript must be Bitcoin::Script' unless subscript.is_a?(Script)
298
+ diff = to_payload.bth.gsub(subscript.to_payload.bth, '')
299
+ Script.parse_from_payload(diff.htb)
300
+ end
301
+
302
+ def ==(other)
303
+ return false unless other
304
+ chunks == other.chunks
305
+ end
306
+
307
+ private
308
+
309
+ # generate p2pkh address. if script dose not p2pkh, return nil.
310
+ def p2pkh_addr
311
+ return nil unless p2pkh?
312
+ hash160 = chunks[2].pushed_data.bth
313
+ return nil unless hash160.htb.bytesize == 20
314
+ hex = Bitcoin.chain_params.address_version + hash160
315
+ Bitcoin.encode_base58_address(hex)
316
+ end
317
+
318
+ # generate p2wpkh address. if script dose not p2wpkh, return nil.
319
+ def p2wpkh_addr
320
+ p2wpkh? ? bech32_addr : nil
321
+ end
322
+
323
+ # generate p2sh address. if script dose not p2sh, return nil.
324
+ def p2sh_addr
325
+ return nil unless p2sh?
326
+ hash160 = chunks[1].pushed_data.bth
327
+ return nil unless hash160.htb.bytesize == 20
328
+ hex = Bitcoin.chain_params.p2sh_version + hash160
329
+ Bitcoin.encode_base58_address(hex)
330
+ end
331
+
332
+ # generate p2wsh address. if script dose not p2wsh, return nil.
333
+ def p2wsh_addr
334
+ p2wsh? ? bech32_addr : nil
335
+ end
336
+
337
+ # return bech32 address for payload
338
+ def bech32_addr
339
+ segwit_addr = Bech32::SegwitAddr.new
340
+ segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
341
+ segwit_addr.script_pubkey = to_payload.bth
342
+ segwit_addr.addr
343
+ end
344
+
345
+ end
346
+
347
+ end