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,170 @@
1
+ module BTC
2
+ # Nodes collect new transactions into a block, hash them into a hash tree,
3
+ # and scan through nonce values to make the block's hash satisfy proof-of-work
4
+ # requirements. When they solve the proof-of-work, they broadcast the block
5
+ # to everyone and the block is added to the block chain. The first transaction
6
+ # in the block is a special one that creates a new coin owned by the creator
7
+ # of the block.
8
+ class Block < BlockHeader
9
+
10
+ # Array of BTC::Transaction objects
11
+ attr_accessor :transactions
12
+
13
+ def self.genesis_mainnet
14
+ self.new(
15
+ version: 1,
16
+ previous_block_hash: ZERO_HASH256,
17
+ merkle_root: BTC::Data.data_from_hex("3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"),
18
+ timestamp: 1231006505,
19
+ bits: 0x1d00ffff,
20
+ nonce: 0x7c2bac1d,
21
+ transactions: [BTC::Transaction.new(
22
+ version: 1,
23
+ inputs: [
24
+ BTC::TransactionInput.new(
25
+ coinbase_data: BTC::Data.data_from_hex("04FFFF001D010445"+
26
+ "5468652054696D65732030332F4A616E2F32303039204368616E63656C6C6F72206F6E2062" +
27
+ "72696E6B206F66207365636F6E64206261696C6F757420666F722062616E6B73"),
28
+ )
29
+ ],
30
+ outputs: [
31
+ BTC::TransactionOutput.new(
32
+ value: 50*COIN,
33
+ script: Script.new(data: BTC::Data.data_from_hex("4104678AFDB0FE5548271967F1"+
34
+ "A67130B7105CD6A828E03909A67962E0EA1F61DEB649F6BC3F4CEF38"+
35
+ "C4F35504E51EC112DE5C384DF7BA0B8D578A4C702B6BF11D5FAC"))
36
+ )
37
+ ],
38
+ lock_time: 0
39
+ )],
40
+ height: 0
41
+ )
42
+ end
43
+
44
+ def self.genesis_testnet
45
+ self.new(
46
+ version: 1,
47
+ previous_block_hash: ZERO_HASH256,
48
+ merkle_root: BTC::Data.data_from_hex("3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"),
49
+ timestamp: 1296688602,
50
+ bits: 0x1d00ffff,
51
+ nonce: 0x18aea41a,
52
+ transactions: [BTC::Transaction.new(
53
+ version: 1,
54
+ inputs: [
55
+ BTC::TransactionInput.new(
56
+ coinbase_data: BTC::Data.data_from_hex("04FFFF001D010445"+
57
+ "5468652054696D65732030332F4A616E2F32303039204368616E63656C6C6F72206F6E2062" +
58
+ "72696E6B206F66207365636F6E64206261696C6F757420666F722062616E6B73"),
59
+ )
60
+ ],
61
+ outputs: [
62
+ BTC::TransactionOutput.new(
63
+ value: 50*COIN,
64
+ script: Script.new(data: BTC::Data.data_from_hex("4104678AFDB0FE5548271967F1"+
65
+ "A67130B7105CD6A828E03909A67962E0EA1F61DEB649F6BC3F4CEF38"+
66
+ "C4F35504E51EC112DE5C384DF7BA0B8D578A4C702B6BF11D5FAC"))
67
+ )
68
+ ],
69
+ lock_time: 0
70
+ )],
71
+ height: 0
72
+ )
73
+ end
74
+
75
+ def initialize(data: nil,
76
+ stream: nil,
77
+ version: CURRENT_VERSION,
78
+ previous_block_hash: nil,
79
+ previous_block_id: nil,
80
+ merkle_root: nil,
81
+ timestamp: 0,
82
+ time: nil,
83
+ bits: 0,
84
+ nonce: 0,
85
+ transactions: [],
86
+
87
+ # optional attributes
88
+ height: nil,
89
+ confirmations: nil)
90
+ super(
91
+ data: data,
92
+ stream: stream,
93
+ version: version,
94
+ previous_block_hash: previous_block_hash,
95
+ previous_block_id: previous_block_id,
96
+ merkle_root: merkle_root,
97
+ timestamp: timestamp,
98
+ time: time,
99
+ bits: bits,
100
+ nonce: nonce,
101
+ height: height,
102
+ confirmations: confirmations
103
+ )
104
+
105
+ @transactions = transactions if transactions
106
+ end
107
+
108
+ def init_with_stream(stream)
109
+ super(stream)
110
+ if !(txs_count = BTC::WireFormat.read_varint(stream: stream).first)
111
+ raise ArgumentError, "Failed to read count of transactions from the stream."
112
+ end
113
+ txs = (0...txs_count).map do
114
+ Transaction.new(stream: stream)
115
+ end
116
+ @transactions = txs
117
+ end
118
+
119
+ def data
120
+ data = super
121
+ data << BTC::WireFormat.encode_varint(self.transactions.size)
122
+ self.transactions.each do |tx|
123
+ data << tx.data
124
+ end
125
+ data
126
+ end
127
+
128
+ def block_header
129
+ BlockHeader.new(
130
+ version: self.version,
131
+ previous_block_hash: self.previous_block_hash,
132
+ merkle_root: self.merkle_root,
133
+ timestamp: self.timestamp,
134
+ bits: self.bits,
135
+ nonce: self.nonce,
136
+ height: self.height,
137
+ confirmations: self.confirmations)
138
+ end
139
+
140
+ def ==(other)
141
+ super(other) && @transactions == other.transactions
142
+ end
143
+
144
+ def dup
145
+ self.class.new(
146
+ version: self.version,
147
+ previous_block_hash: self.previous_block_hash,
148
+ merkle_root: self.merkle_root,
149
+ timestamp: self.timestamp,
150
+ bits: self.bits,
151
+ nonce: self.nonce,
152
+ transactions: self.transactions.map{|t|t.dup},
153
+ height: self.height,
154
+ confirmations: self.confirmations)
155
+ end
156
+
157
+ def inspect
158
+ %{#<#{self.class.name}:#{self.block_id[0,24]}} +
159
+ %{ ver:#{self.version}} +
160
+ %{ prev:#{self.previous_block_id[0,24]}} +
161
+ %{ merkle_root:#{BTC.id_from_hash(self.merkle_root)[0,16]}} +
162
+ %{ timestamp:#{self.timestamp}} +
163
+ %{ bits:0x#{self.bits.to_s(16)}} +
164
+ %{ nonce:0x#{self.nonce.to_s(16)}} +
165
+ %{ txs(#{self.transactions.size}): #{self.transactions.inspect}} +
166
+ %{>}
167
+ end
168
+
169
+ end
170
+ end
@@ -0,0 +1,231 @@
1
+ module BTC
2
+ # Block header is the 80-byte prefix of the block.
3
+ # Nodes collect new transactions into a block, hash them into a hash tree,
4
+ # and scan through nonce values to make the block's hash satisfy proof-of-work
5
+ # requirements. When they solve the proof-of-work, they broadcast the block
6
+ # to everyone and the block is added to the block chain. The first transaction
7
+ # in the block is a special one that creates a new coin owned by the creator
8
+ # of the block.
9
+ class BlockHeader
10
+
11
+ CURRENT_VERSION = 2
12
+ ZERO_HASH256 = "\x00".b*32
13
+
14
+ # Binary hash of the block
15
+ attr_reader :block_hash
16
+
17
+ # Hex big-endian hash of the block
18
+ attr_reader :block_id
19
+
20
+ # Block version.
21
+ attr_accessor :version
22
+
23
+ # Binary hash of the previous block
24
+ attr_accessor :previous_block_hash
25
+
26
+ # Hex big-endian hash of the previous block
27
+ attr_accessor :previous_block_id
28
+
29
+ # Raw binary root hash of the transaction merkle tree.
30
+ attr_accessor :merkle_root
31
+
32
+ # uint32 unix timestamp
33
+ attr_accessor :timestamp
34
+
35
+ # Time object derived from timestamp
36
+ attr_accessor :time
37
+
38
+ # uint32 proof-of-work in compact format
39
+ attr_accessor :bits
40
+
41
+ # uint32 nonce (used for mining iterations)
42
+ attr_accessor :nonce
43
+
44
+
45
+ # Optional attributes.
46
+ # These are not derived from block data, but attached externally (e.g. via external APIs).
47
+
48
+ # The distance from the first block in the chain (genesis block has height 0).
49
+ attr_accessor :height
50
+
51
+ # The number of blocks that have been processed since the previous block (including the block itself).
52
+ attr_accessor :confirmations
53
+
54
+ attr_accessor :my_name
55
+
56
+ def self.genesis_mainnet
57
+ self.new(
58
+ version: 1,
59
+ previous_block_hash: ZERO_HASH256,
60
+ merkle_root: BTC::Data.data_from_hex("3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"),
61
+ timestamp: 1231006505,
62
+ bits: 0x1d00ffff,
63
+ nonce: 0x7c2bac1d,
64
+ height: 0
65
+ )
66
+ end
67
+
68
+ def self.genesis_testnet
69
+ self.new(
70
+ version: 1,
71
+ previous_block_hash: ZERO_HASH256,
72
+ merkle_root: BTC::Data.data_from_hex("3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"),
73
+ timestamp: 1296688602,
74
+ bits: 0x1d00ffff,
75
+ nonce: 0x18aea41a,
76
+ height: 0
77
+ )
78
+ end
79
+
80
+ def self.with_data(data)
81
+ raise ArgumentError, "Use #{self}.new(data: ...) instead"
82
+ end
83
+
84
+ def self.with_stream(stream)
85
+ raise ArgumentError, "Use #{self}.new(stream: ...) instead"
86
+ end
87
+
88
+ def initialize(data: nil,
89
+ stream: nil,
90
+ version: CURRENT_VERSION,
91
+ previous_block_hash: nil,
92
+ previous_block_id: nil,
93
+ merkle_root: nil,
94
+ timestamp: 0,
95
+ time: nil,
96
+ bits: 0,
97
+ nonce: 0,
98
+
99
+ # optional attributes
100
+ height: nil,
101
+ confirmations: nil)
102
+
103
+ if stream || data
104
+ init_with_stream(stream || StringIO.new(data))
105
+ else
106
+ @version = version || CURRENT_VERSION
107
+ @previous_block_hash = previous_block_hash || ZERO_HASH256
108
+ @previous_block_hash = BTC.hash_from_id(previous_block_id) if previous_block_id
109
+ @merkle_root = merkle_root || ZERO_HASH256
110
+ @timestamp = timestamp || 0
111
+ @timestamp = time.to_i if time
112
+ @bits = bits || 0
113
+ @nonce = nonce || 0
114
+ end
115
+
116
+ @height = height
117
+ @confirmations = confirmations
118
+ end
119
+
120
+ def init_with_stream(stream)
121
+ raise ArgumentError, "Stream is missing" if !stream
122
+ if stream.eof?
123
+ raise ArgumentError, "Can't parse block header from stream because it is already closed."
124
+ end
125
+
126
+ if !(version = BTC::WireFormat.read_int32le(stream: stream).first)
127
+ raise ArgumentError, "Failed to read block version prefix from the stream."
128
+ end
129
+
130
+ if !(prevhash = stream.read(32)) || prevhash.bytesize != 32
131
+ raise ArgumentError, "Failed to read 32-byte previous_block_hash from the stream."
132
+ end
133
+
134
+ if !(mrklroot = stream.read(32)) || mrklroot.bytesize != 32
135
+ raise ArgumentError, "Failed to read 32-byte block merkle_root from the stream."
136
+ end
137
+
138
+ if !(timestamp = BTC::WireFormat.read_uint32le(stream: stream).first)
139
+ raise ArgumentError, "Failed to read 32-byte block timestamp from the stream."
140
+ end
141
+
142
+ if !(bits = BTC::WireFormat.read_uint32le(stream: stream).first)
143
+ raise ArgumentError, "Failed to read 32-byte proof-of-work bits from the stream."
144
+ end
145
+
146
+ if !(nonce = BTC::WireFormat.read_uint32le(stream: stream).first)
147
+ raise ArgumentError, "Failed to read 32-byte nonce from the stream."
148
+ end
149
+
150
+ @version = version
151
+ @previous_block_hash = prevhash
152
+ @merkle_root = mrklroot
153
+ @timestamp = timestamp
154
+ @bits = bits
155
+ @nonce = nonce
156
+ end
157
+
158
+ def block_hash
159
+ BTC.hash256(self.header_data)
160
+ end
161
+
162
+ def block_id
163
+ BTC.id_from_hash(self.block_hash)
164
+ end
165
+
166
+ def previous_block_id
167
+ BTC.id_from_hash(self.previous_block_hash)
168
+ end
169
+
170
+ def previous_block_id=(block_id)
171
+ self.previous_block_hash = BTC.hash_from_id(block_id)
172
+ end
173
+
174
+ def time
175
+ Time.at(self.timestamp).utc
176
+ end
177
+
178
+ def time=(time)
179
+ self.timestamp = time.to_i
180
+ end
181
+
182
+ def data
183
+ header_data
184
+ end
185
+
186
+ def header_data # so that in subclass Block we don't hash the entire block
187
+ data = "".b
188
+ data << BTC::WireFormat.encode_int32le(self.version)
189
+ data << self.previous_block_hash
190
+ data << self.merkle_root
191
+ data << BTC::WireFormat.encode_uint32le(self.timestamp)
192
+ data << BTC::WireFormat.encode_uint32le(self.bits)
193
+ data << BTC::WireFormat.encode_uint32le(self.nonce)
194
+ data
195
+ end
196
+
197
+ def ==(other)
198
+ @version == other.version &&
199
+ @previous_block_hash == other.previous_block_hash &&
200
+ @merkle_root == other.merkle_root &&
201
+ @timestamp == other.timestamp &&
202
+ @bits == other.bits &&
203
+ @nonce == other.nonce
204
+ end
205
+ alias_method :eql?, :==
206
+
207
+ def dup
208
+ self.class.new(
209
+ version: self.version,
210
+ previous_block_hash: self.previous_block_hash,
211
+ merkle_root: self.merkle_root,
212
+ timestamp: self.timestamp,
213
+ bits: self.bits,
214
+ nonce: self.nonce,
215
+ height: self.height,
216
+ confirmations: self.confirmations)
217
+ end
218
+
219
+ def inspect
220
+ %{#<#{self.class.name}:#{self.block_id[0,24]}} +
221
+ %{ ver:#{self.version}} +
222
+ %{ prev:#{self.previous_block_id[0,24]}} +
223
+ %{ merkle_root:#{BTC.id_from_hash(self.merkle_root)[0,16]}} +
224
+ %{ timestamp:#{self.timestamp}} +
225
+ %{ bits:0x#{self.bits.to_s(16)}} +
226
+ %{ nonce:0x#{self.nonce.to_s(16)}} +
227
+ %{>}
228
+ end
229
+
230
+ end
231
+ end
@@ -0,0 +1,59 @@
1
+ module BTC
2
+
3
+ # Satoshi is the smallest unit representable in Bitcoin transactions.
4
+ SATOSHI = 1
5
+
6
+ # 100 mln satoshis is one Bitcoin
7
+ COIN = 100_000_000
8
+
9
+ # 1 bit is 100 satoshis (1 millionth of a bitcoin)
10
+ BIT = 100
11
+
12
+ # Network Rules (changing these will result in incompatibility with other nodes)
13
+
14
+ # The maximum allowed size for a serialized block, in bytes
15
+ MAX_BLOCK_SIZE = 1000000
16
+
17
+ # The maximum allowed number of signature check operations in a block
18
+ MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50
19
+
20
+ # No amount larger than this (in satoshis) is valid
21
+ MAX_MONEY = 21_000_000 * COIN
22
+
23
+ # Coinbase transaction outputs can only be spent after this number of new blocks
24
+ COINBASE_MATURITY = 100
25
+
26
+ # Threshold for BTC::Transaction#lock_time: below this value it is interpreted
27
+ # as a block number, otherwise as UNIX timestamp.
28
+ LOCKTIME_THRESHOLD = 500000000 # Tue Nov 5 00:53:20 1985 UTC (max block number is in year ≈11521)
29
+
30
+ # P2SH BIP16 didn't become active until Apr 1 2012.
31
+ # All txs before this timestamp should not be verified with P2SH rule.
32
+ BIP16_TIMESTAMP = 1333238400
33
+
34
+ # Scripts longer than 10000 bytes are invalid.
35
+ MAX_SCRIPT_SIZE = 10000
36
+
37
+ # Maximum number of bytes per "pushdata" operation
38
+ MAX_SCRIPT_ELEMENT_SIZE = 520 # bytes
39
+
40
+ # Number of public keys allowed for OP_CHECKMULTISIG
41
+ MAX_KEYS_FOR_CHECKMULTISIG = 20
42
+
43
+ # Maximum number of operations allowed per script (excluding pushdata operations and OP_<N>)
44
+ # Multisig op additionally increases count by a number of pubkeys.
45
+ MAX_OPS_PER_SCRIPT = 201
46
+
47
+ # Soft Rules (can bend these without becoming incompatible with everyone)
48
+
49
+ # The maximum number of entries in an 'inv' protocol message
50
+ MAX_INV_SZ = 50000
51
+
52
+ # The maximum size for mined blocks
53
+ MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE / 2
54
+
55
+ # The maximum size for transactions we're willing to relay/mine
56
+ MAX_STANDARD_TX_SIZE = MAX_BLOCK_SIZE_GEN / 5
57
+
58
+
59
+ end
@@ -0,0 +1,64 @@
1
+ require 'bigdecimal'
2
+
3
+ module BTC
4
+ # Modeled after NSNumberFormatter in Cocoa, this class allows to convert
5
+ # bitcoin amounts to their string representations and vice versa.
6
+ class CurrencyFormatter
7
+
8
+ STYLE_BTC = :btc # 1.0 is 1 btc (100_000_000 satoshis)
9
+ STYLE_BTC_LONG = :btc_long # 1.00000000 is 1 btc (100_000_000 satoshis)
10
+ STYLE_MBTC = :mbtc # 1.0 is 0.001 btc (100_000 satoshis)
11
+ STYLE_BIT = :bit # 1.0 is 0.000001 btc (100 satoshis)
12
+ STYLE_SATOSHIS = :satoshis # 1.0 is 0.00000001 btc (1 satoshi)
13
+
14
+ attr_accessor :style
15
+ attr_accessor :show_suffix
16
+
17
+ # Returns a singleton formatter for BTC values (1.0 is one bitcoin) without suffix.
18
+ def self.btc_formatter
19
+ @btc_formatter ||= self.new(style: STYLE_BTC)
20
+ end
21
+
22
+ # Returns a singleton formatter for BTC values where there are always 8 places
23
+ # after decimal point (e.g. "42.00000000").
24
+ def self.btc_long_formatter
25
+ @btc_long_formatter ||= self.new(style: STYLE_BTC_LONG)
26
+ end
27
+
28
+ def initialize(style: :btc, show_suffix: false)
29
+ @style = style
30
+ @show_suffix = show_suffix
31
+ end
32
+
33
+ # Returns formatted string for an amount in satoshis.
34
+ def string_from_number(number)
35
+ if @style == :btc
36
+ number = number.to_i
37
+ return "#{number / BTC::COIN}.#{'%08d' % [number % BTC::COIN]}".gsub(/0+$/,"").gsub(/\.$/,".0")
38
+ elsif @style == :btc_long
39
+ number = number.to_i
40
+ return "#{number / BTC::COIN}.#{'%08d' % [number % BTC::COIN]}"
41
+ else
42
+ # TODO: parse other styles
43
+ raise "Not implemented"
44
+ end
45
+ end
46
+
47
+ # Returns amount of satoshis parsed from a formatted string according to style attribute.
48
+ def number_from_string(string)
49
+ bd = BigDecimal.new(string)
50
+ if @style == :btc || @style == :btc_long
51
+ return (bd * BTC::COIN).to_i
52
+ else
53
+ # TODO: support other styles
54
+ raise "Not Implemented"
55
+ end
56
+ end
57
+
58
+ # Creates a copy if you want to customize another formatter (e.g. a global singleton like btc_formatter)
59
+ def dup
60
+ self.class.new(style: @style, show_suffix: @show_suffix)
61
+ end
62
+
63
+ end # BitcoinFormatter
64
+ end # BTC
@@ -0,0 +1,98 @@
1
+ require 'securerandom'
2
+
3
+ module BTC
4
+ module Data
5
+ extend self
6
+ include HashFunctions # obsolete, left for backwards compatibility
7
+
8
+ HEX_PACK_CODE = "H*".freeze
9
+ BYTE_PACK_CODE = "C*".freeze
10
+
11
+ # Generates a secure random number of a given length
12
+ def random_data(length = 32)
13
+ SecureRandom.random_bytes(length)
14
+ end
15
+
16
+ # Converts hexadecimal string to a binary data string.
17
+ def data_from_hex(hex_string)
18
+ raise ArgumentError, "Hex string is missing" if !hex_string
19
+ hex_string = hex_string.strip
20
+ data = [hex_string].pack(HEX_PACK_CODE)
21
+ if hex_from_data(data) != hex_string.downcase # invalid hex string was detected
22
+ raise FormatError, "Hex string is invalid: #{hex_string.inspect}"
23
+ end
24
+ return data
25
+ end
26
+
27
+ # Converts binary string to lowercase hexadecimal representation.
28
+ def hex_from_data(data)
29
+ raise ArgumentError, "Data is missing" if !data
30
+ return data.unpack(HEX_PACK_CODE).first
31
+ end
32
+
33
+ # Converts a binary string to an array of bytes (list of integers).
34
+ # Returns a much more efficient slice of bytes if offset/limit or
35
+ # range are specified. That is, avoids converting the entire buffer to byte array.
36
+ #
37
+ # Note 1: if range is specified, it takes precedence over offset/limit.
38
+ #
39
+ # Note 2: byteslice(...).bytes is less efficient as it creates
40
+ # an intermediate shorter string.
41
+ #
42
+ def bytes_from_data(data, offset: 0, limit: nil, range: nil)
43
+ raise ArgumentError, "Data is missing" if !data
44
+ if offset == 0 && limit == nil && range == nil
45
+ return data.bytes
46
+ end
47
+ if range
48
+ offset = range.begin
49
+ limit = range.size
50
+ end
51
+ bytes = []
52
+ data.each_byte do |byte|
53
+ if offset > 0
54
+ offset -= 1
55
+ else
56
+ if !limit || limit > 0
57
+ bytes << byte
58
+ limit -= 1 if limit
59
+ else
60
+ break
61
+ end
62
+ end
63
+ end
64
+ bytes
65
+ end
66
+
67
+ # Converts binary string to an array of bytes (list of integers).
68
+ def data_from_bytes(bytes)
69
+ raise ArgumentError, "Bytes are missing" if !bytes
70
+ bytes.pack(BYTE_PACK_CODE)
71
+ end
72
+
73
+ # Returns string as-is if it is ASCII-compatible
74
+ # (that is, if you are interested in 7-bit characters exposed as #bytes).
75
+ # If it is not, attempts to transcode to UTF8 replacing invalid characters if there are any.
76
+ # If options are not specified, uses safe default that replaces unknown characters with standard character.
77
+ # If options are specified, they are used as-is for String#encode method.
78
+ def ensure_ascii_compatible_encoding(string, options = nil)
79
+ if string.encoding.ascii_compatible?
80
+ string
81
+ else
82
+ string.encode(Encoding::UTF_8, options || {:invalid => :replace, :undef => :replace})
83
+ end
84
+ end
85
+
86
+ # Returns string as-is if it is already encoded in binary encoding (aka BINARY or ASCII-8BIT).
87
+ # If it is not, converts to binary by calling stdlib's method #b.
88
+ def ensure_binary_encoding(string)
89
+ raise ArgumentError, "String is missing" if !string
90
+ if string.encoding == Encoding::BINARY
91
+ string
92
+ else
93
+ string.b
94
+ end
95
+ end
96
+
97
+ end
98
+ end