btcruby 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -0
  3. data/.travis.yml +7 -0
  4. data/FAQ.md +7 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +18 -0
  7. data/HOWTO.md +17 -0
  8. data/LICENSE +19 -0
  9. data/README.md +59 -0
  10. data/Rakefile +6 -0
  11. data/TODO.txt +40 -0
  12. data/bin/console +19 -0
  13. data/btcruby.gemspec +20 -0
  14. data/documentation/address.md +73 -0
  15. data/documentation/base58.md +52 -0
  16. data/documentation/block.md +127 -0
  17. data/documentation/block_header.md +120 -0
  18. data/documentation/constants.md +88 -0
  19. data/documentation/data.md +54 -0
  20. data/documentation/diagnostics.md +90 -0
  21. data/documentation/extensions.md +76 -0
  22. data/documentation/hash_functions.md +58 -0
  23. data/documentation/hash_id.md +22 -0
  24. data/documentation/index.md +230 -0
  25. data/documentation/key.md +177 -0
  26. data/documentation/keychain.md +180 -0
  27. data/documentation/network.md +75 -0
  28. data/documentation/opcode.md +220 -0
  29. data/documentation/openssl.md +7 -0
  30. data/documentation/p2pkh.md +71 -0
  31. data/documentation/p2sh.md +64 -0
  32. data/documentation/proof_of_work.md +84 -0
  33. data/documentation/script.md +280 -0
  34. data/documentation/signature.md +71 -0
  35. data/documentation/transaction.md +213 -0
  36. data/documentation/transaction_builder.md +188 -0
  37. data/documentation/transaction_input.md +133 -0
  38. data/documentation/transaction_output.md +130 -0
  39. data/documentation/wif.md +72 -0
  40. data/documentation/wire_format.md +70 -0
  41. data/lib/btcruby/address.rb +296 -0
  42. data/lib/btcruby/base58.rb +108 -0
  43. data/lib/btcruby/big_number.rb +47 -0
  44. data/lib/btcruby/block.rb +170 -0
  45. data/lib/btcruby/block_header.rb +231 -0
  46. data/lib/btcruby/constants.rb +59 -0
  47. data/lib/btcruby/currency_formatter.rb +64 -0
  48. data/lib/btcruby/data.rb +98 -0
  49. data/lib/btcruby/diagnostics.rb +92 -0
  50. data/lib/btcruby/errors.rb +8 -0
  51. data/lib/btcruby/extensions.rb +65 -0
  52. data/lib/btcruby/hash_functions.rb +54 -0
  53. data/lib/btcruby/hash_id.rb +18 -0
  54. data/lib/btcruby/key.rb +517 -0
  55. data/lib/btcruby/keychain.rb +464 -0
  56. data/lib/btcruby/network.rb +73 -0
  57. data/lib/btcruby/opcode.rb +197 -0
  58. data/lib/btcruby/open_assets/asset.rb +35 -0
  59. data/lib/btcruby/open_assets/asset_address.rb +49 -0
  60. data/lib/btcruby/open_assets/asset_definition.rb +75 -0
  61. data/lib/btcruby/open_assets/asset_id.rb +24 -0
  62. data/lib/btcruby/open_assets/asset_marker.rb +94 -0
  63. data/lib/btcruby/open_assets/asset_processor.rb +377 -0
  64. data/lib/btcruby/open_assets/asset_transaction.rb +184 -0
  65. data/lib/btcruby/open_assets/asset_transaction_builder/errors.rb +15 -0
  66. data/lib/btcruby/open_assets/asset_transaction_builder/provider.rb +32 -0
  67. data/lib/btcruby/open_assets/asset_transaction_builder/result.rb +47 -0
  68. data/lib/btcruby/open_assets/asset_transaction_builder.rb +418 -0
  69. data/lib/btcruby/open_assets/asset_transaction_input.rb +64 -0
  70. data/lib/btcruby/open_assets/asset_transaction_output.rb +140 -0
  71. data/lib/btcruby/open_assets.rb +26 -0
  72. data/lib/btcruby/openssl.rb +536 -0
  73. data/lib/btcruby/proof_of_work.rb +110 -0
  74. data/lib/btcruby/safety.rb +26 -0
  75. data/lib/btcruby/script.rb +733 -0
  76. data/lib/btcruby/signature_hashtype.rb +37 -0
  77. data/lib/btcruby/transaction.rb +511 -0
  78. data/lib/btcruby/transaction_builder/errors.rb +15 -0
  79. data/lib/btcruby/transaction_builder/provider.rb +54 -0
  80. data/lib/btcruby/transaction_builder/result.rb +73 -0
  81. data/lib/btcruby/transaction_builder/signer.rb +28 -0
  82. data/lib/btcruby/transaction_builder.rb +520 -0
  83. data/lib/btcruby/transaction_input.rb +298 -0
  84. data/lib/btcruby/transaction_outpoint.rb +30 -0
  85. data/lib/btcruby/transaction_output.rb +315 -0
  86. data/lib/btcruby/version.rb +3 -0
  87. data/lib/btcruby/wif.rb +118 -0
  88. data/lib/btcruby/wire_format.rb +362 -0
  89. data/lib/btcruby.rb +44 -2
  90. data/sample_code/creating_a_p2sh_multisig_address.rb +21 -0
  91. data/sample_code/creating_a_transaction_manually.rb +44 -0
  92. data/sample_code/generating_an_address.rb +20 -0
  93. data/sample_code/using_transaction_builder.rb +49 -0
  94. data/spec/address_spec.rb +206 -0
  95. data/spec/all.rb +6 -0
  96. data/spec/base58_spec.rb +83 -0
  97. data/spec/block_header_spec.rb +18 -0
  98. data/spec/block_spec.rb +18 -0
  99. data/spec/currency_formatter_spec.rb +46 -0
  100. data/spec/data_spec.rb +50 -0
  101. data/spec/diagnostics_spec.rb +41 -0
  102. data/spec/key_spec.rb +205 -0
  103. data/spec/keychain_spec.rb +261 -0
  104. data/spec/network_spec.rb +48 -0
  105. data/spec/open_assets/asset_address_spec.rb +33 -0
  106. data/spec/open_assets/asset_id_spec.rb +15 -0
  107. data/spec/open_assets/asset_marker_spec.rb +47 -0
  108. data/spec/open_assets/asset_processor_spec.rb +567 -0
  109. data/spec/open_assets/asset_transaction_builder_spec.rb +273 -0
  110. data/spec/open_assets/asset_transaction_spec.rb +70 -0
  111. data/spec/proof_of_work_spec.rb +53 -0
  112. data/spec/script_spec.rb +66 -0
  113. data/spec/spec_helper.rb +8 -0
  114. data/spec/transaction_builder_spec.rb +338 -0
  115. data/spec/transaction_spec.rb +162 -0
  116. data/spec/wire_format_spec.rb +283 -0
  117. metadata +141 -7
@@ -0,0 +1,118 @@
1
+ module BTC
2
+ # Private key in Wallet Import Format (WIF aka Sipa format).
3
+ # Examples: 5KQntKuhYWSRXNq... or L3p8oAcQTtuokSC...
4
+ class WIF < Address
5
+
6
+ KEY_LENGTH = 32
7
+
8
+ def self.mainnet_version
9
+ 128
10
+ end
11
+
12
+ def self.testnet_version
13
+ 239
14
+ end
15
+
16
+ attr_accessor :public_key_compressed
17
+ def public_key_compressed?
18
+ @public_key_compressed
19
+ end
20
+
21
+ def key
22
+ BTC::Key.new(private_key: self.private_key, public_key_compressed: @public_key_compressed, network: self.network)
23
+ end
24
+
25
+ def private_key
26
+ @data
27
+ end
28
+
29
+ def public_address
30
+ self.key.address(network: self.network)
31
+ end
32
+
33
+ def ==(other)
34
+ return false if !other
35
+ self.data == other.data &&
36
+ self.version == other.version &&
37
+ self.public_key_compressed == other.public_key_compressed
38
+ end
39
+ alias_method :eql?, :==
40
+
41
+ # private API
42
+ def self.with_string(base58check_string, raw_data = nil)
43
+ raise ArgumentError, "Use WIF.new(string: ...) instead"
44
+ end
45
+
46
+ # Creates an address with private key data and non-compressed pubkey flag.
47
+ def self.with_data(data, public_key_compressed: false, network: nil)
48
+ raise ArgumentError, "Use WIF.new(private_key: ...) or WIF.new(key: ...) instead"
49
+ end
50
+
51
+ # Usage:
52
+ # * WIF.new(string: ...)
53
+ # * WIF.new(private_key: ..., public_key_compressed: true|false, network: ...)
54
+ # * WIF.new(key: ...)
55
+ def initialize(string: nil,
56
+ data: nil,
57
+ network: nil,
58
+ _raw_data: nil,
59
+ private_key: nil,
60
+ key: nil,
61
+ public_key_compressed: nil)
62
+ if key
63
+ raise ArgumentError, "Key must contain private_key to be exported in WIF" if !key.private_key
64
+ private_key = key.private_key
65
+ if public_key_compressed == nil
66
+ public_key_compressed = key.public_key_compressed
67
+ end
68
+ network ||= key.network
69
+ end
70
+ if string
71
+ if data || private_key || key || (public_key_compressed != nil) || network
72
+ raise ArgumentError, "Cannot specify individual attributes when decoding WIF from string"
73
+ end
74
+ _raw_data ||= Base58.data_from_base58check(string)
75
+ if _raw_data.bytesize != (1 + KEY_LENGTH) && _raw_data.bytesize != (2 + KEY_LENGTH)
76
+ raise FormatError, "Raw WIF data should have size #{1 + KEY_LENGTH}(+1), but it is #{_raw_data.bytesize} instead"
77
+ end
78
+ # compressed flag is simply one more byte appended to the string
79
+ @base58check_string = string
80
+ @data = _raw_data[1, KEY_LENGTH]
81
+ @public_key_compressed = (_raw_data.bytesize == (2 + KEY_LENGTH))
82
+ @version = _raw_data.bytes.first
83
+ @network = nil
84
+ elsif data ||= private_key
85
+ if data.bytesize != KEY_LENGTH
86
+ raise FormatError, "Failed to create WIF: data should have size #{KEY_LENGTH}, but it is #{data.bytesize} instead"
87
+ end
88
+ @base58check_string = nil
89
+ @data = data
90
+ @public_key_compressed = public_key_compressed
91
+ if @public_key_compressed == nil
92
+ @public_key_compressed = false # legacy default is uncompressed pubkey
93
+ end
94
+ @version = nil
95
+ @network = network
96
+ else
97
+ raise ArgumentError, "Either data or string must be provided"
98
+ end
99
+ end
100
+
101
+ def data_for_base58check_encoding
102
+ data = BTC::Data.data_from_bytes([self.version]) + @data
103
+ if @public_key_compressed
104
+ data += BTC::Data.data_from_bytes([0x01])
105
+ end
106
+ return data
107
+ end
108
+
109
+ def inspect
110
+ %{#<#{self.class}:#{to_s} privkey:#{BTC::Data.hex_from_data(data)} (#{@public_key_compressed ? '' : 'un'}compressed pubkey)>}
111
+ end
112
+
113
+ end
114
+
115
+ # For compatibility
116
+ PrivateKeyAddress = WIF
117
+
118
+ end
@@ -0,0 +1,362 @@
1
+ # A collection of routines dealing with parsing and writing protocol messages.
2
+ # Various structures have a variable-length data prepended by a length prefix which is itself of variable length.
3
+ # This length prefix is a variable-length integer, varint (aka "CompactSize").
4
+ #
5
+ # NB. varint refers to https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer and is what Satoshi called "CompactSize"
6
+ # BitcoinQT has later added even more compact format called CVarInt to use in its local block storage. CVarInt is not implemented here.
7
+ #
8
+ # Value Storage length Format
9
+ # < 0xfd 1 uint8_t
10
+ # <= 0xffff 3 0xfd followed by the value as little endian uint16_t
11
+ # <= 0xffffffff 5 0xfe followed by the value as little endian uint32_t
12
+ # > 0xffffffff 9 0xff followed by the value as little endian uint64_t
13
+ #
14
+ module BTC
15
+ module WireFormat
16
+ extend self
17
+
18
+ # Reads varint from data or stream.
19
+ # Either data or stream must be present (and only one of them).
20
+ # Optional offset is useful when reading from data.
21
+ # Returns [value, length] where value is a decoded integer value and length is number of bytes read (including offset bytes).
22
+ # Value may be nil when decoding failed (length might be zero or greater, depending on how much data was consumed before failing).
23
+ # Usage:
24
+ # i, _ = read_varint(data: buffer, offset: 42)
25
+ # i, _ = read_varint(stream: File.open('someblock','r'))
26
+ def read_varint(data: nil, stream: nil, offset: 0)
27
+ if data && !stream
28
+ return [nil, 0] if data.bytesize < 1 + offset
29
+
30
+ bytes = BTC::Data.bytes_from_data(data, offset: offset, limit: 9) # we don't need more than 9 bytes.
31
+
32
+ byte = bytes[0]
33
+
34
+ if byte < 0xfd
35
+ return [byte, offset + 1]
36
+ elsif byte == 0xfd
37
+ return [nil, 1] if data.bytesize < 3 + offset # 1 byte prefix, 2 bytes uint16
38
+ return [bytes[1] +
39
+ bytes[2]*256, offset + 3]
40
+ elsif byte == 0xfe
41
+ return [nil, 1] if data.bytesize < 5 + offset # 1 byte prefix, 4 bytes uint32
42
+ return [bytes[1] +
43
+ bytes[2]*256 +
44
+ bytes[3]*256*256 +
45
+ bytes[4]*256*256*256, offset + 5]
46
+ elsif byte == 0xff
47
+ return [nil, 1] if data.bytesize < 9 + offset # 1 byte prefix, 8 bytes uint64
48
+ return [bytes[1] +
49
+ bytes[2]*256 +
50
+ bytes[3]*256*256 +
51
+ bytes[4]*256*256*256 +
52
+ bytes[5]*256*256*256*256 +
53
+ bytes[6]*256*256*256*256*256 +
54
+ bytes[7]*256*256*256*256*256*256 +
55
+ bytes[8]*256*256*256*256*256*256*256, offset + 9]
56
+ end
57
+
58
+ elsif stream && !data
59
+
60
+ if stream.eof?
61
+ raise ArgumentError, "Can't parse varint from stream because it is already closed."
62
+ end
63
+
64
+ if offset > 0
65
+ buf = stream.read(offset)
66
+ return [nil, 0] if !buf
67
+ return [nil, buf.bytesize] if buf.bytesize < offset
68
+ end
69
+
70
+ prefix = stream.read(1)
71
+
72
+ return [nil, offset] if !prefix || prefix.bytesize == 0
73
+
74
+ byte = prefix.bytes[0]
75
+
76
+ if byte < 0xfd
77
+ return [byte, offset + 1]
78
+ elsif byte == 0xfd
79
+ buf = stream.read(2)
80
+ return [nil, offset + 1] if !buf
81
+ return [nil, offset + 1 + buf.bytesize] if buf.bytesize < 2
82
+ return [buf.unpack("v").first, offset + 3]
83
+ elsif byte == 0xfe
84
+ buf = stream.read(4)
85
+ return [nil, offset + 1] if !buf
86
+ return [nil, offset + 1 + buf.bytesize] if buf.bytesize < 4
87
+ return [buf.unpack("V").first, offset + 5]
88
+ elsif byte == 0xff
89
+ buf = stream.read(8)
90
+ return [nil, offset + 1] if !buf
91
+ return [nil, offset + 1 + buf.bytesize] if buf.bytesize < 8
92
+ return [buf.unpack("Q<").first, offset + 9]
93
+ end
94
+
95
+ else
96
+ raise ArgumentError, "Either data or stream must be specified."
97
+ end
98
+ end # read_varint
99
+
100
+ # Encodes integer and returns its binary varint representation.
101
+ def encode_varint(i)
102
+ raise ArgumentError, "int must be present" if !i
103
+ raise ArgumentError, "int must be non-negative" if i < 0
104
+
105
+ buf = if i < 0xfd
106
+ [i].pack("C")
107
+ elsif i <= 0xffff
108
+ [0xfd, i].pack("Cv")
109
+ elsif i <= 0xffffffff
110
+ [0xfe, i].pack("CV")
111
+ elsif i <= 0xffffffffffffffff
112
+ [0xff, i].pack("CQ<")
113
+ else
114
+ raise ArgumentError, "Does not support integers larger 0xffffffffffffffff (i = 0x#{i.to_s(16)})"
115
+ end
116
+
117
+ buf
118
+ end
119
+
120
+ # Encodes integer and returns its binary varint representation.
121
+ # If data is given, appends to a data.
122
+ # If stream is given, writes to a stream.
123
+ def write_varint(i, data: nil, stream: nil)
124
+ buf = encode_varint(i)
125
+ data << buf if data
126
+ stream.write(buf) if stream
127
+ buf
128
+ end
129
+
130
+ # Reads variable-length string from data buffer or IO stream.
131
+ # Either data or stream must be present (and only one of them).
132
+ # Returns [string, length] where length is a number of bytes read (includes length prefix and offset bytes).
133
+ # In case of failure, returns [nil, length] where length is a number of bytes read before the error was encountered.
134
+ def read_string(data: nil, stream: nil, offset: 0)
135
+ if data && !stream
136
+
137
+ string_length, read_length = read_varint(data: data, offset: offset)
138
+
139
+ # If failed to read the length prefix, return nil.
140
+ return [nil, read_length] if !string_length
141
+
142
+ # Check if we have enough bytes to read the string itself
143
+ return [nil, read_length] if data.bytesize < read_length + string_length
144
+
145
+ string = BTC::Data.ensure_binary_encoding(data)[read_length, string_length]
146
+
147
+ return [string, read_length + string_length]
148
+
149
+ elsif stream && !data
150
+
151
+ string_length, read_length = read_varint(stream: stream, offset: offset)
152
+ return [nil, read_length] if !string_length
153
+
154
+ buf = stream.read(string_length)
155
+
156
+ return [nil, read_length] if !buf
157
+ return [nil, read_length + buf.bytesize] if buf.bytesize < string_length
158
+
159
+ return [buf, read_length + buf.bytesize]
160
+ else
161
+ raise ArgumentError, "Either data or stream must be specified."
162
+ end
163
+ end
164
+
165
+ # Returns the binary representation of the var-length string.
166
+ def encode_string(string)
167
+ raise ArgumentError, "String must be present" if !string
168
+ encode_varint(string.bytesize) + BTC::Data.ensure_binary_encoding(string)
169
+ end
170
+
171
+ # Writes variable-length string to a data buffer or IO stream.
172
+ # If data is given, appends to a data.
173
+ # If stream is given, writes to a stream.
174
+ # Returns the binary representation of the var-length string.
175
+ def write_string(string, data: nil, stream: nil)
176
+ raise ArgumentError, "String must be present" if !string
177
+
178
+ intbuf = write_varint(string.bytesize, data: data, stream: stream)
179
+
180
+ stringbuf = BTC::Data.ensure_binary_encoding(string)
181
+
182
+ data << stringbuf if data
183
+ stream.write(stringbuf) if stream
184
+
185
+ intbuf + stringbuf
186
+ end
187
+
188
+
189
+ # LEB128 encoding used in Open Assets protocol
190
+
191
+ # Decodes an unsigned integer encoded in LEB128.
192
+ # Returns `[value, length]` where `value` is an integer decoded from LEB128 and `length`
193
+ # is a number of bytes read (includes length prefix and offset bytes).
194
+ def read_uleb128(data: nil, stream: nil, offset: 0)
195
+ if (data && stream) || (!data && !stream)
196
+ raise ArgumentError, "Either data or stream must be specified."
197
+ end
198
+ if data
199
+ data = BTC::Data.ensure_binary_encoding(data)
200
+ end
201
+ if stream
202
+ if stream.eof?
203
+ raise ArgumentError, "Can't read LEB128 from stream because it is already closed."
204
+ end
205
+ if offset > 0
206
+ buf = stream.read(offset)
207
+ return [nil, 0] if !buf
208
+ return [nil, buf.bytesize] if buf.bytesize < offset
209
+ end
210
+ end
211
+ result = 0
212
+ shift = 0
213
+ while true
214
+ byte = if data
215
+ return [nil, offset] if data.bytesize < 1 + offset
216
+ BTC::Data.bytes_from_data(data, offset: offset, limit: 1)[0]
217
+ elsif stream
218
+ buf = stream.read(1)
219
+ return [nil, offset] if !buf || buf.bytesize == 0
220
+ buf.bytes[0]
221
+ end
222
+ result |= (byte & 0x7f) << shift
223
+ break if byte & 0x80 == 0
224
+ shift += 7
225
+ offset += 1
226
+ end
227
+ [result, offset + 1]
228
+ end
229
+
230
+ # Encodes an unsigned integer using LEB128.
231
+ def encode_uleb128(value)
232
+ raise ArgumentError, "Signed integers are not supported" if value < 0
233
+ return "\x00" if value == 0
234
+ bytes = []
235
+ while value != 0
236
+ byte = value & 0b01111111 # 0x7f
237
+ value >>= 7
238
+ if value != 0
239
+ byte |= 0b10000000 # 0x80
240
+ end
241
+ bytes << byte
242
+ end
243
+ return BTC::Data.data_from_bytes(bytes)
244
+ end
245
+
246
+ # Writes an unsigned integer encoded in LEB128 to a data buffer or a stream.
247
+ # Returns LEB128-encoded binary string.
248
+ def write_uleb128(value, data: nil, stream: nil)
249
+ raise ArgumentError, "Integer must be present" if !value
250
+ buf = encode_uleb128(value)
251
+ data << buf if data
252
+ stream.write(buf) if stream
253
+ buf
254
+ end
255
+
256
+
257
+
258
+ PACK_FORMAT_UINT8 = "C".freeze
259
+ PACK_FORMAT_INT8 = "c".freeze
260
+ PACK_FORMAT_UINT16LE = "S<".freeze
261
+ PACK_FORMAT_INT16LE = "s<".freeze
262
+ PACK_FORMAT_UINT32LE = "L<".freeze
263
+ PACK_FORMAT_INT32LE = "l<".freeze
264
+ PACK_FORMAT_UINT32BE = "L>".freeze # used in BIP32
265
+ PACK_FORMAT_UINT64LE = "Q<".freeze
266
+ PACK_FORMAT_INT64LE = "q<".freeze
267
+
268
+ # These read fixed-length integer in appropriate format ("le" stands for "little-endian")
269
+ # Return [value, length] or [nil, length] just like #read_varint method (see above).
270
+ def read_uint8(data: nil, stream: nil, offset: 0)
271
+ _read_fixint(name: :uint8, length: 1, pack_format: PACK_FORMAT_UINT8, data: data, stream: stream, offset: offset)
272
+ end
273
+
274
+ def read_int8(data: nil, stream: nil, offset: 0)
275
+ _read_fixint(name: :int8, length: 1, pack_format: PACK_FORMAT_INT8, data: data, stream: stream, offset: offset)
276
+ end
277
+
278
+ def read_uint16le(data: nil, stream: nil, offset: 0)
279
+ _read_fixint(name: :uint16le, length: 2, pack_format: PACK_FORMAT_UINT16LE, data: data, stream: stream, offset: offset)
280
+ end
281
+
282
+ def read_int16le(data: nil, stream: nil, offset: 0)
283
+ _read_fixint(name: :int16le, length: 2, pack_format: PACK_FORMAT_INT16LE, data: data, stream: stream, offset: offset)
284
+ end
285
+
286
+ def read_uint32le(data: nil, stream: nil, offset: 0)
287
+ _read_fixint(name: :uint32le, length: 4, pack_format: PACK_FORMAT_UINT32LE, data: data, stream: stream, offset: offset)
288
+ end
289
+
290
+ def read_int32le(data: nil, stream: nil, offset: 0)
291
+ _read_fixint(name: :int32le, length: 4, pack_format: PACK_FORMAT_INT32LE, data: data, stream: stream, offset: offset)
292
+ end
293
+
294
+ def read_uint32be(data: nil, stream: nil, offset: 0) # used in BIP32
295
+ _read_fixint(name: :uint32be, length: 4, pack_format: PACK_FORMAT_UINT32BE, data: data, stream: stream, offset: offset)
296
+ end
297
+
298
+ def read_uint64le(data: nil, stream: nil, offset: 0)
299
+ _read_fixint(name: :uint64le, length: 8, pack_format: PACK_FORMAT_UINT64LE, data: data, stream: stream, offset: offset)
300
+ end
301
+
302
+ def read_int64le(data: nil, stream: nil, offset: 0)
303
+ _read_fixint(name: :int64le, length: 8, pack_format: PACK_FORMAT_INT64LE, data: data, stream: stream, offset: offset)
304
+ end
305
+
306
+ # Encode int into one of the formats
307
+ def encode_uint8(int); [int].pack(PACK_FORMAT_UINT8); end
308
+ def encode_int8(int); [int].pack(PACK_FORMAT_INT8); end
309
+ def encode_uint16le(int); [int].pack(PACK_FORMAT_UINT16LE); end
310
+ def encode_int16le(int); [int].pack(PACK_FORMAT_INT16LE); end
311
+ def encode_uint32le(int); [int].pack(PACK_FORMAT_UINT32LE); end
312
+ def encode_int32le(int); [int].pack(PACK_FORMAT_INT32LE); end
313
+ def encode_uint32be(int); [int].pack(PACK_FORMAT_UINT32BE); end # used in BIP32
314
+ def encode_uint64le(int); [int].pack(PACK_FORMAT_UINT64LE); end
315
+ def encode_int64le(int); [int].pack(PACK_FORMAT_INT64LE); end
316
+
317
+
318
+ protected
319
+
320
+ def _read_fixint(name: nil, length: nil, pack_format: nil, data: nil, stream: nil, offset: 0)
321
+ if data && !stream
322
+
323
+ if data.bytesize < offset + length
324
+ Diagnostics.current.add_message("BTC::WireFormat#read_#{name}: Not enough bytes to read #{name} in binary string.")
325
+ return [nil, 0]
326
+ end
327
+
328
+ if offset > 0
329
+ pack_format = "@#{offset}" + pack_format
330
+ end
331
+
332
+ return [data.unpack(pack_format).first, offset + length]
333
+
334
+ elsif stream && !data
335
+
336
+ if offset > 0
337
+ buf = stream.read(offset)
338
+ return [nil, 0] if !buf
339
+ return [nil, buf.bytesize] if buf.bytesize < offset
340
+ end
341
+
342
+ buf = stream.read(length)
343
+
344
+ if !buf
345
+ Diagnostics.current.add_message("BTC::WireFormat#read_#{name}: Failed to read #{name} from stream.")
346
+ return [nil, offset]
347
+ end
348
+
349
+ if buf.bytesize < length
350
+ Diagnostics.current.add_message("BTC::WireFormat#read_#{name}: Not enough bytes to read #{name} from stream.")
351
+ return [nil, offset + buf.bytesize]
352
+ end
353
+
354
+ return [buf.unpack(pack_format).first, offset + length]
355
+
356
+ else
357
+ raise ArgumentError, "BTC::WireFormat#read_#{name}: Either data or stream must be specified."
358
+ end
359
+ end
360
+
361
+ end
362
+ end
data/lib/btcruby.rb CHANGED
@@ -1,2 +1,44 @@
1
- module BTC
2
- end
1
+
2
+ require 'ffi' # gem install ffi
3
+
4
+ # Tip: import 'btcruby/extensions' to enable extensions to standard classes (e.g. String#to_hex)
5
+ # Extensions are not imported by default to avoid conflicts with other libraries.
6
+
7
+ require_relative 'btcruby/version.rb'
8
+ require_relative 'btcruby/errors.rb'
9
+ require_relative 'btcruby/diagnostics.rb'
10
+ require_relative 'btcruby/safety.rb'
11
+ require_relative 'btcruby/hash_functions.rb'
12
+ require_relative 'btcruby/data.rb'
13
+ require_relative 'btcruby/openssl.rb'
14
+ require_relative 'btcruby/big_number.rb'
15
+ require_relative 'btcruby/base58.rb'
16
+
17
+ require_relative 'btcruby/constants.rb'
18
+ require_relative 'btcruby/currency_formatter.rb'
19
+ require_relative 'btcruby/network.rb'
20
+ require_relative 'btcruby/address.rb'
21
+ require_relative 'btcruby/wif.rb'
22
+ require_relative 'btcruby/key.rb'
23
+ require_relative 'btcruby/keychain.rb'
24
+ require_relative 'btcruby/wire_format.rb'
25
+ require_relative 'btcruby/hash_id.rb'
26
+ require_relative 'btcruby/transaction.rb'
27
+ require_relative 'btcruby/transaction_input.rb'
28
+ require_relative 'btcruby/transaction_output.rb'
29
+ require_relative 'btcruby/transaction_outpoint.rb'
30
+ require_relative 'btcruby/script.rb'
31
+ require_relative 'btcruby/opcode.rb'
32
+ require_relative 'btcruby/signature_hashtype.rb'
33
+ require_relative 'btcruby/transaction_builder.rb'
34
+ require_relative 'btcruby/proof_of_work.rb'
35
+ require_relative 'btcruby/block_header.rb'
36
+ require_relative 'btcruby/block.rb'
37
+ require_relative 'btcruby/open_assets.rb'
38
+
39
+ # TODO:
40
+ # require_relative 'btcruby/curve_point.rb'
41
+ # require_relative 'btcruby/script_machine.rb'
42
+ # require_relative 'btcruby/merkle_block.rb'
43
+ # require_relative 'btcruby/bloom_filter.rb'
44
+ # require_relative 'btcruby/processor.rb'
@@ -0,0 +1,21 @@
1
+ # Creating a P2SH multisig address
2
+ # --------------------------------
3
+ #
4
+ # To create a P2SH multisig address you will need a set of public keys.
5
+ # In the example below we generate three random keys and compose 2-of-3 multisig script
6
+ # which is then transformed into a P2SH address. To redeem from this address you will need
7
+ # not only two signatures, but also the original multisig script.
8
+
9
+ require_relative "../lib/btcruby.rb"
10
+
11
+ keys = [BTC::Key.random, BTC::Key.random, BTC::Key.random]
12
+ pubkeys = keys.map(&:public_key)
13
+
14
+ multisig_script = BTC::Script.multisig_script(public_keys: pubkeys, signatures_required: 2)
15
+ puts multisig_script.to_s # => "OP_2 03e4e14a... 03b4b3f7... 030fa2ec... OP_3 OP_CHECKMULTISIG"
16
+
17
+ p2sh_script = multisig_script.p2sh_script
18
+ puts p2sh_script.to_s # => "OP_HASH160 26f5b7ad4e890c07b8c55fc551e39d6693c5e984 OP_EQUAL"
19
+
20
+ address = p2sh_script.standard_address
21
+ puts address.to_s # => 35F1xaoodzRZUBJHi6TgA85qPjXQcW8XsQ
@@ -0,0 +1,44 @@
1
+ # Creating a transaction manually
2
+ # -------------------------------
3
+ #
4
+ # To manually create a transaction, you will need to specify raw inputs,
5
+ # compute the signature and compose a signature script for each input and
6
+ # take care of calculating a change amount correctly.
7
+
8
+ require_relative "../lib/btcruby.rb"
9
+
10
+ include BTC
11
+
12
+ tx = Transaction.new
13
+
14
+ # 1. Add a raw input with previous transaction ID and output index.
15
+ tx.add_input(TransactionInput.new(
16
+ previous_id: "aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31",
17
+ previous_index: 0))
18
+
19
+ # 2. Add a raw output with a script
20
+ tx.add_output(TransactionOutput.new(
21
+ value: 100_000,
22
+ script: PublicKeyAddress.with_string("1CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG").script))
23
+
24
+ # 3. Get the private key from WIF
25
+ key = Key.new(wif: "L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy")
26
+
27
+ # 4. Sign the input (assuming it links to an output with address 18oxCAnbuKHDjP7KzLBDj8mLjggDBjE1Q9)
28
+ hashtype = BTC::SIGHASH_ALL
29
+ sighash = tx.signature_hash(input_index: 0,
30
+ output_script: PublicKeyAddress.with_string("18oxCAnbuKHDjP7KzLBDj8mLjggDBjE1Q9").script,
31
+ hash_type: hashtype)
32
+ tx.inputs[0].signature_script = Script.new << (key.ecdsa_signature(sighash) + WireFormat.encode_uint8(hashtype)) << key.public_key
33
+
34
+ # Get transaction data and broadcast it
35
+ puts "Binary transaction:"
36
+ puts tx.data # => raw binary data
37
+ puts "Hex transaction:"
38
+ puts tx.to_hex # hex-encoded data
39
+ # => 0100000001313eb630b128102b60241ca895f1d0ffca2170d5a0990e094f2182c102ab94aa
40
+ # 000000006a473044022039148258144202301221a305adb38ce0a182ecb4055c6015cdd735
41
+ # 8372d7ad6d022008aa259c87177f0e4e887dd0947c57fd140eb8f8a826f14ef8389dbc26ef
42
+ # a7b20121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59f
43
+ # ffffffff01a0860100000000001976a9147ab89f9fae3f8043dcee5f7b5467a0f0a6e2f7e1
44
+ # 88ac00000000
@@ -0,0 +1,20 @@
1
+ # Generating an address
2
+ # ---------------------
3
+ #
4
+ # This example demonstrates how to generate a key and get its address.
5
+
6
+ require_relative "../lib/btcruby.rb"
7
+
8
+ key = BTC::Key.random
9
+
10
+ puts key.to_wif # private key in WIF format
11
+ # => L4RqZhbn2VsVgy2wCWW8kUPpA4xEkH7WbfPtj1MdFug5MayHzLeT
12
+
13
+ puts key.address.to_s # public address
14
+ # => 1MFqAcAxNsAKj5e6yksZCCyfNukSdDGsEY
15
+
16
+ puts key.to_wif(network: BTC::Network.testnet)
17
+ # => cUnq2cbdTZZkrQWCavKG7ntsnJFeQjDCfhYMqRp8m2L5cL1yHDmc
18
+
19
+ puts key.address(network: BTC::Network.testnet).to_s
20
+ # => n1mnTfFwBtbaWC7ihKqw28BzEuM9YqxRyw
@@ -0,0 +1,49 @@
1
+ # Using transaction builder
2
+ # -------------------------
3
+ #
4
+ # Transaction builder helps composing arbitrary transactions using just keys or unspent outputs.
5
+ # It takes care of computing a proper change amount, adding fees and signing inputs.
6
+ # It is also highly customizable, so you may use it for very complex transactions.
7
+
8
+ require_relative "../lib/btcruby.rb"
9
+
10
+ builder = BTC::TransactionBuilder.new
11
+
12
+ # 1. Provide a list of address to get unspent outputs from.
13
+ # If address is a WIF instance, it will be used to sign corresponding input
14
+ # If address is a public address (or P2SH), its input will remain unsigned.
15
+ builder.input_addresses = [ BTC::Key.new(wif: "L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy").to_wif_object ]
16
+
17
+ # 2. Use external API (e.g. Chain.com) to fetch unspent outputs for the input addresses.
18
+ # In this example we simply hard-code a single unspent output.
19
+ # Note: transaction ID and output index must be provided.
20
+ builder.unspent_outputs_provider_block = lambda do |addresses, outputs_amount, outputs_size, fee|
21
+ txout = BTC::TransactionOutput.new(
22
+ value: 50_000,
23
+ script: BTC::PublicKeyAddress.with_string("17XBj6iFEsf8kzDMGQk5ghZipxX49VXuaV").script,
24
+ transaction_id: "115e8f72f39fad874cfab0deed11a80f24f967a84079fb56ddf53ea02e308986",
25
+ index: 0
26
+ )
27
+ [ txout ]
28
+ end
29
+
30
+ # 3. Specify payment address and amount
31
+ builder.outputs = [ BTC::TransactionOutput.new(
32
+ value: 10_000,
33
+ script: BTC::PublicKeyAddress.with_string("17XBj6iFEsf8kzDMGQk5ghZipxX49VXuaV").script) ]
34
+
35
+ # 4. Specify the change address
36
+ builder.change_address = BTC::PublicKeyAddress.with_string("1CBtcGivXmHQ8ZqdPgeMfcpQNJrqTrSAcG")
37
+
38
+ # 5. Build the transaction and broadcast it.
39
+ result = builder.build
40
+ tx = result.transaction
41
+ puts tx.to_hex
42
+
43
+ # => 01000000018689302ea03ef5dd56fb7940a867f9240fa811eddeb0fa4c87ad9ff3728f5e11
44
+ # 000000006b483045022100e280f71106a84a4a1b1a2035eae70266eb53630beab2b59cc8cf
45
+ # f40b1a5bdbb902201dcbae9bb12730fe5563dc37e3a33e064f2efa78ba0af5c0179187aece
46
+ # 180b6c0121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b5
47
+ # 9fffffffff0210270000000000001976a91447862fe165e6121af80d5dde1ecb478ed17056
48
+ # 5b88ac30750000000000001976a9147ab89f9fae3f8043dcee5f7b5467a0f0a6e2f7e188ac
49
+ # 00000000