openassets-ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fa13af5ba17c13c7516e29819722ffc2a7a69766
4
+ data.tar.gz: d94cc426ab8af56d2e0967f415323c67de17a6f7
5
+ SHA512:
6
+ metadata.gz: 3ebf299dbc652bffa5299bb23ab2cf32862f78cdadfcb010adf8d6a013a680d62b397fac19703de464612fa041ab01fd91959943414c65c9af04b869b3e07b20
7
+ data.tar.gz: d338cb28973172b73696a1acef698028d54904fef2fdebe7e5148478eff2acea596f65218c185808c35e1f76157b26d651b382347a11a7e991f80cad9831de32
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .idea
11
+ *.iml
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ openassets-ruby
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.2.2
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in openassets-ruby.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 HAW International Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 azuchi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # openassets-ruby
2
+ The implementation of the Open Assets Protocol for Ruby.
3
+
4
+ ## Configuration
5
+
6
+ Initialize the connection information to the Bitcoin Core server.
7
+
8
+ ```ruby
9
+ require 'openassets'
10
+
11
+ api = OpenAssets::Api.new({:network => 'mainnet',
12
+ :provider => 'bitcoind',
13
+ :dust_limit => 600,
14
+ :rpc => {:user => 'xxx', :password => 'xxx', :schema => 'http', :port => 8332, :host => 'localhost'}})
15
+ ```
16
+
17
+ ## API
18
+
19
+ Currently openassets-ruby support the following API.
20
+ (The other API is in development. ex, send_asset)
21
+
22
+ * **list_unspent**
23
+ Returns an array of unspent transaction outputs, argument with the asset ID and quantity of each output.
24
+ ```ruby
25
+ # get all unspent outputs in the wallet.
26
+ api.list_unspent
27
+
28
+ # specify th open asset address.
29
+ api.list_unspent(['akTfC7D825Cse4NvFiLCy7vr3B6x2Mpq8t6'])
30
+ ```
31
+
32
+ * **get_balance**
33
+ Returns the balance in both bitcoin and colored coin assets for all of the addresses available in your Bitcoin Core wallet.
34
+ ```ruby
35
+ # get all balance in the wallet.
36
+ api.get_balance
37
+
38
+ # specify the open asset address.
39
+ api.get_balance('akTfC7D825Cse4NvFiLCy7vr3B6x2Mpq8t6')
40
+ ```
41
+
42
+ * **issue_asset**
43
+ Creates a transaction for issuing an asset.
44
+ ```ruby
45
+ # issue asset
46
+ # api.issue_asset(<issuer open asset address>, <issuing asset quantity>, <metadata>, <to open asset address>, <fees (The fess in satoshis for the transaction. use 10000 satoshi if specified nil)>, <mode=('broadcast', 'signed', 'unsigned')>)
47
+
48
+ # example
49
+ address = 'akEJwzkzEFau4t2wjbXoMs7MwtZkB8xixmH'
50
+ api.issue_asset(address, 150, 'u=https://goo.gl/bmVEuw', address, nil, 'broadcast')
51
+ ```
52
+
53
+ ## License
54
+
55
+ The MIT License (MIT)
56
+
57
+ Copyright (c) 2015 HAW International Inc.
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining a copy
60
+ of this software and associated documentation files (the "Software"), to deal
61
+ in the Software without restriction, including without limitation the rights
62
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
63
+ copies of the Software, and to permit persons to whom the Software is
64
+ furnished to do so, subject to the following conditions:
65
+
66
+ The above copyright notice and this permission notice shall be included in all
67
+ copies or substantial portions of the Software.
68
+
69
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
70
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
71
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
72
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
73
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
74
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
75
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new("spec")
5
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "openassets"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,229 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module OpenAssets
4
+
5
+ class Api
6
+
7
+ include OpenAssets::Util
8
+
9
+ attr_reader :config
10
+ attr_reader :provider
11
+ attr_reader :cache
12
+
13
+ def initialize(config = nil)
14
+ @cache = {}
15
+ @config = {:network => 'mainnet',
16
+ :provider => 'bitcoind',
17
+ :dust_limit => 600, :default_fees => 10000,
18
+ :rpc => { :host => 'localhost', :port => 8332 , :user => '', :password => '', :schema => 'https'}}
19
+ if config
20
+ @config.update(config)
21
+ end
22
+ if @config[:provider] == 'bitcoind'
23
+ @provider = Provider::BitcoinCoreProvider.new(@config[:rpc])
24
+ else
25
+ raise StandardError, 'specified unsupported provider.'
26
+ end
27
+ end
28
+
29
+ def provider
30
+ @provider
31
+ end
32
+
33
+ def is_testnet?
34
+ @config[:network] == 'testnet'
35
+ end
36
+
37
+ # get UTXO for colored coins.
38
+ # @param [Array] oa_address Obtain the balance of this open assets address only, or all addresses if unspecified.
39
+ # @return [Array] Return array of the unspent information Hash.
40
+ def list_unspent(oa_address = [])
41
+ outputs = get_unspent_outputs([])
42
+ result = outputs.map {|out|
43
+ address = script_to_address(out.output.script)
44
+ script = out.output.script.to_payload.bth
45
+ {
46
+ 'txid' => out.out_point.hash,
47
+ 'vout' => out.out_point.index,
48
+ 'address' => address,
49
+ 'oa_address' => address.nil? ? nil : address_to_oa_address(address),
50
+ 'script' => script,
51
+ 'amount' => satoshi_to_coin(out.output.value),
52
+ 'confirmations' => out.confirmations,
53
+ 'asset_id' => out.output.asset_id,
54
+ 'asset_quantity' => out.output.asset_quantity.to_s
55
+ }
56
+ }
57
+ oa_address.empty? ? result : result.select{|r|oa_address.include?(r['oa_address'])}
58
+ end
59
+
60
+ # Returns the balance in both bitcoin and colored coin assets for all of the addresses available in your Bitcoin Core wallet.
61
+ # @param [String] address The open assets address. if unspecified nil.
62
+ def get_balance(address = nil)
63
+ outputs = get_unspent_outputs(address.nil? ? [] : [oa_address_to_address(address)])
64
+ colored_outputs = outputs.map{|o|o.output}
65
+ sorted_outputs = colored_outputs.sort_by { |o|o.script.to_string}
66
+ groups = sorted_outputs.group_by{|o| o.script.to_string}
67
+ result = groups.map{|k, v|
68
+ btc_address = script_to_address(v[0].script)
69
+ sorted_script_outputs = v.sort_by{|o|o.asset_id unless o.asset_id}
70
+ group_assets = sorted_script_outputs.group_by{|o|o.asset_id}.select{|k,v| !k.nil?}
71
+ assets = group_assets.map{|asset_id, outputs|
72
+ {
73
+ 'asset_id' => asset_id,
74
+ 'quantity' => outputs.inject(0) { |sum, o| sum + o.asset_quantity }.to_s
75
+ }
76
+ }
77
+ {
78
+ 'address' => btc_address,
79
+ 'oa_address' => btc_address.nil? ? nil : address_to_oa_address(btc_address),
80
+ 'value' => satoshi_to_coin(v.inject(0) { |sum, o|sum + o.value}),
81
+ 'assets' => assets
82
+ }
83
+ }
84
+ address.nil? ? result : result.select{|r|r['oa_address'] == address}
85
+ end
86
+
87
+ # Creates a transaction for issuing an asset.
88
+ # @param[String] from The open asset address to issue the asset from.
89
+ # @param[Integer] amount The amount of asset units to issue.
90
+ # @param[String] to The open asset address to send the asset to; if unspecified, the assets are sent back to the issuing address.
91
+ # @param[String] metadata The metadata to embed in the transaction. The asset definition pointer defined by this metadata.
92
+ # @param[Integer] fees The fess in satoshis for the transaction.
93
+ # @param[String] mode Specify the following mode.
94
+ # 'broadcast' (default) for signing and broadcasting the transaction,
95
+ # 'signed' for signing the transaction without broadcasting,
96
+ # 'unsigned' for getting the raw unsigned transaction without broadcasting"""='broadcast'
97
+ def issue_asset(from, amount, metadata = nil, to = nil, fees = nil, mode = 'broadcast')
98
+ to = from if to.nil?
99
+ builder = OpenAssets::Transaction::TransactionBuilder.new(@config[:dust_limit])
100
+ colored_outputs = get_unspent_outputs([oa_address_to_address(from)])
101
+ issue_param = OpenAssets::Transaction::TransferParameters.new(colored_outputs, to, from, amount)
102
+ tx = builder.issue_asset(issue_param, metadata, fees.nil? ? @config[:default_fees]: fees)
103
+ tx = process_transaction(tx, mode)
104
+ tx
105
+ end
106
+
107
+ # Get unspent outputs.
108
+ # @param [Array] addresses The array of Bitcoin address.
109
+ # @return [Array[OpenAssets::Transaction::SpendableOutput]] The array of unspent outputs.
110
+ def get_unspent_outputs(addresses)
111
+ validate_address(addresses)
112
+ unspent = provider.list_unspent(addresses)
113
+ result = unspent.map{|item|
114
+ output_result = get_output(item['txid'], item['vout'])
115
+ output = OpenAssets::Transaction::SpendableOutput.new(
116
+ OpenAssets::Transaction::OutPoint.new(item['txid'], item['vout']), output_result)
117
+ output.confirmations = item['confirmations']
118
+ output
119
+ }
120
+ result
121
+ end
122
+
123
+ def get_output(txid, output_index)
124
+ cached_output = @cache[txid + output_index.to_s]
125
+ return cached_output if cached_output
126
+ decode_tx = provider.get_transaction(txid, 0)
127
+ raise OpenAssets::Transaction::TransactionBuildError, "txid #{txid} could not be retrieved." if decode_tx.nil?
128
+ tx = Bitcoin::Protocol::Tx.new(decode_tx.htb)
129
+ colored_outputs = get_color_transaction(tx)
130
+ colored_outputs.each_with_index { |o, index | @cache[txid + index.to_s] = o}
131
+ colored_outputs[output_index]
132
+ end
133
+
134
+ def get_color_transaction(tx)
135
+ unless tx.is_coinbase?
136
+ tx.outputs.each_with_index { |out, i|
137
+ marker_output_payload = OpenAssets::Protocol::MarkerOutput.parse_script(out.pk_script)
138
+ unless marker_output_payload.nil?
139
+ marker_output = OpenAssets::Protocol::MarkerOutput.deserialize_payload(marker_output_payload)
140
+ inputs = tx.inputs.map {|input|
141
+ get_output(input.previous_output, input.prev_out_index)
142
+ }
143
+ asset_ids = compute_asset_ids(inputs, i, tx.outputs, marker_output.asset_quantities)
144
+ return asset_ids unless asset_ids.nil?
145
+ end
146
+ }
147
+ end
148
+ tx.outputs.map{|out| OpenAssets::Protocol::TransactionOutput.new(out.value, out.parsed_script, nil, 0, OpenAssets::Protocol::OutputType::UNCOLORED)}
149
+ end
150
+
151
+ private
152
+ # @param [Array[OpenAssets::Protocol::TransactionOutput] inputs The outputs referenced by the inputs of the transaction.
153
+ # @param [Integer] marker_output_index The position of the marker output in the transaction.
154
+ # @param [Array[Bitcoin::Protocol::TxOUt]] outputs The outputs of the transaction.
155
+ # @param [Array[Integer]] asset_quantities The list of asset quantities of the outputs.
156
+ def compute_asset_ids(inputs, marker_output_index, outputs, asset_quantities)
157
+ return nil if asset_quantities.length > outputs.length - 1 || inputs.length == 0
158
+ result = []
159
+
160
+ # Add the issuance outputs
161
+ issuance_asset_id = pubkey_hash_to_asset_id(inputs[0].script.get_hash160)
162
+
163
+ for i in (0..marker_output_index-1)
164
+ value = outputs[i].value
165
+ script = outputs[i].parsed_script
166
+ if i < asset_quantities.length && asset_quantities[i] > 0
167
+ output = OpenAssets::Protocol::TransactionOutput.new(value, script, issuance_asset_id, asset_quantities[i], OpenAssets::Protocol::OutputType::ISSUANCE)
168
+ else
169
+ output = OpenAssets::Protocol::TransactionOutput.new(value, script, nil, 0, OpenAssets::Protocol::OutputType::ISSUANCE)
170
+ end
171
+ result << output
172
+ end
173
+
174
+ # Add the marker output
175
+ marker_output = outputs[marker_output_index]
176
+ result << OpenAssets::Protocol::TransactionOutput.new(marker_output.value, marker_output.parsed_script, nil, 0, OpenAssets::Protocol::OutputType::MARKER_OUTPUT)
177
+
178
+ # Add the transfer outputs
179
+ input_enum = inputs.each
180
+ input_units_left = 0
181
+ index = 0
182
+ for i in (marker_output_index + 1)..(outputs.length-1)
183
+ output_asset_quantity = (i <= asset_quantities.length) ? asset_quantities[i-1] : 0
184
+ output_units_left = output_asset_quantity
185
+ asset_id = nil
186
+ while output_units_left > 0
187
+ index += 1
188
+ if input_units_left == 0
189
+ begin
190
+ current_input = input_enum.next
191
+ input_units_left = current_input.asset_quantity
192
+ rescue StopIteration => e
193
+ return nil
194
+ end
195
+ end
196
+ unless current_input.asset_id.nil?
197
+ progress = [input_units_left, output_units_left].min
198
+ output_units_left -= progress
199
+ input_units_left -= progress
200
+ if asset_id.nil?
201
+ # This is the first input to map to this output
202
+ asset_id = current_input.asset_id
203
+ elsif asset_id != current_input.asset_id
204
+ return nil
205
+ end
206
+ end
207
+ end
208
+ result << OpenAssets::Protocol::TransactionOutput.new(outputs[i].value, outputs[i].parsed_script,
209
+ asset_id, output_asset_quantity, OpenAssets::Protocol::OutputType::TRANSFER)
210
+ end
211
+ result
212
+ end
213
+
214
+ def process_transaction(tx, mode)
215
+ if mode == 'broadcast' || mode == 'signed'
216
+ # sign the transaction
217
+ signed_tx = provider.sign_transaction(tx.to_payload.bth)
218
+ if mode == 'broadcast'
219
+ puts provider.send_transaction(signed_tx.to_payload.bth)
220
+ end
221
+ signed_tx
222
+ else
223
+ tx
224
+ end
225
+ end
226
+
227
+ end
228
+
229
+ end
@@ -0,0 +1,5 @@
1
+ module OpenAssets
2
+ class Error < StandardError
3
+
4
+ end
5
+ end
@@ -0,0 +1,82 @@
1
+ module OpenAssets
2
+ module Protocol
3
+
4
+ class MarkerOutput
5
+ include OpenAssets::Util
6
+ extend OpenAssets::Util
7
+
8
+ MAX_ASSET_QUANTITY = 2 ** 63 -1
9
+
10
+ # A tag indicating thath this transaction is an Open Assets transaction.
11
+ OAP_MARKER = "4f41"
12
+ # The major revision number of the Open Assets Protocol.(1=0x0100)
13
+ VERSION = "0100"
14
+
15
+ attr_accessor :asset_quantities
16
+ attr_accessor :metadata
17
+
18
+ # @param [Array] asset_quantities The asset quantity array
19
+ # @param [String] metadata The metadata in the marker output.
20
+ def initialize(asset_quantities, metadata)
21
+ @asset_quantities = asset_quantities
22
+ @metadata = metadata
23
+ end
24
+
25
+ # Serialize the marker output into a Open Assets Payload buffer.
26
+ # @return [String] The serialized payload.
27
+ def to_payload
28
+ payload = [OAP_MARKER, VERSION]
29
+ payload << Bitcoin::Protocol.pack_var_int(@asset_quantities.length).unpack("H*")
30
+ @asset_quantities.map{|q|payload << encode_leb128(q)}
31
+ payload << Bitcoin::Protocol.pack_var_int(@metadata.length).unpack("H*")
32
+ tmp = []
33
+ @metadata.bytes{|b| tmp << b.to_s(16)}
34
+ payload << tmp.join
35
+ payload.join
36
+ end
37
+
38
+ # Deserialize the marker output payload.
39
+ # @param [String] payload The Open Assets Payload.
40
+ # @return [OpenAssets::Protocol::MarkerOutput] The marker output object.
41
+ def self.deserialize_payload(payload)
42
+ payload = payload[8..-1] # exclude OAP_MARKER,VERSION
43
+ asset_quantity, payload = parse_asset_qty(payload)
44
+ list = to_bytes(payload).map{|x|(x.to_i(16)>=128 ? x : x+"|")}.join.split('|')[0..(asset_quantity - 1)].join
45
+ asset_quantities = decode_leb128(list)
46
+ meta = to_bytes(payload[list.size..-1])
47
+ metadata = meta[1..-1].map{|x|x.to_i(16).chr}.join
48
+ new(asset_quantities, metadata)
49
+ end
50
+
51
+ # Parses an output and returns the payload if the output matches the right pattern for a marker output,
52
+ # @param [Bitcoin::Script] output_script: The output script to be parsed.
53
+ # @return [String] The byte string of the marker output payload if the output fits the pattern, nil otherwise.
54
+ def self.parse_script(output_script)
55
+ data = Bitcoin::Script.new(output_script).get_op_return_data
56
+ return data if data.nil?
57
+ data.start_with?(OAP_MARKER) ? data : nil
58
+ end
59
+
60
+ # Creates an output script containing an OP_RETURN and a PUSHDATA from payload.
61
+ # @return [Bitcoin::Script] the output script.
62
+ def build_script
63
+ Bitcoin::Script.from_string("OP_RETURN #{to_payload}")
64
+ end
65
+
66
+ private
67
+ def self.parse_asset_qty(payload)
68
+ bytes = to_bytes(payload)
69
+ case bytes[0]
70
+ when "fd" then
71
+ [(bytes[1]+bytes[2]).to_i(16), payload[6..-1]]
72
+ when "fe" then
73
+ [(bytes[1]+bytes[2]+bytes[3]+bytes[4]).to_i(16),payload[10..-1]]
74
+ else
75
+ [bytes[0].to_i(16),payload[2..-1]]
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,17 @@
1
+ # Transaction output type enum
2
+ module OpenAssets
3
+ module Protocol
4
+ module OutputType
5
+ UNCOLORED = 0
6
+ MARKER_OUTPUT = 1
7
+ ISSUANCE = 2
8
+ TRANSFER = 3
9
+
10
+ # get all enum.
11
+ def self.all
12
+ self.constants.map{|name|self.const_get(name)}
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ module OpenAssets
2
+ module Protocol
3
+
4
+ # Represents a transaction output and its asset ID and asset quantity.
5
+ class TransactionOutput
6
+ attr_accessor :value
7
+ attr_accessor :script
8
+ attr_accessor :asset_id
9
+ attr_accessor :asset_quantity
10
+ attr_accessor :output_type
11
+
12
+ # @param [Integer] value The satoshi value of the output.
13
+ # @param [Bitcoin::Script] script The script controlling redemption of the output.
14
+ # @param [String] asset_id The asset ID of the output.
15
+ # @param [Integer] asset_quantity The asset quantity of the output.
16
+ # @param [OpenAssets::Transaction::OutPutType] output_type The type of the output.
17
+ def initialize(value, script, asset_id = nil, asset_quantity = 0, output_type = OutputType::UNCOLORED)
18
+ raise ArgumentError, "invalid output_type : #{output_type}" unless OutputType.all.include?(output_type)
19
+ raise ArgumentError, "invalid asset_quantity asset_quantity should be unsignd integer. " unless asset_quantity.between?(0, MarkerOutput::MAX_ASSET_QUANTITY)
20
+ @value = value
21
+ @script = script
22
+ @asset_id = asset_id
23
+ @asset_quantity = asset_quantity
24
+ @output_type = output_type
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module OpenAssets
2
+ module Protocol
3
+ autoload :MarkerOutput, 'openassets/protocol/marker_output'
4
+ autoload :TransactionOutput, 'openassets/protocol/transaction_output'
5
+ autoload :OutputType, 'openassets/protocol/output_type'
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module OpenAssets
2
+ module Provider
3
+
4
+ class ApiError < StandardError
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,96 @@
1
+ require 'rest-client'
2
+
3
+ module OpenAssets
4
+ module Provider
5
+
6
+ # The implementation of BlockChain provider using Bitcoin Core.
7
+ class BitcoinCoreProvider < BlockChainProviderBase
8
+
9
+ attr_reader :config
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ # Get an array of unspent transaction outputs belonging to this wallet.
16
+ # @param [Array] addresses If present, only outputs which pay an address in this array will be returned.
17
+ # @param [Integer] min The minimum number of confirmations the transaction containing an output must have in order to be returned. Default is 1.
18
+ # @param [Integer] max The maximum number of confirmations the transaction containing an output may have in order to be returned. Default is 9999999.
19
+ def list_unspent(addresses = [], min = 1 , max = 9999999)
20
+ request('listunspent', min, max, addresses)
21
+ end
22
+
23
+ # Get raw transaction.
24
+ # @param [String] transaction_hash The transaction hash.
25
+ # @param [String] verbose Whether to get the serialized or decoded transaction. 0: serialized transaction (Default). 1: decode transaction.
26
+ # @return [String] (if verbose=0)—the serialized transaction. (if verbose=1)—the decoded transaction. (if transaction not found)—nil.
27
+ def get_transaction(transaction_hash, verbose = 0)
28
+ begin
29
+ request('getrawtransaction', transaction_hash, verbose)
30
+ rescue OpenAssets::Provider::ApiError => e
31
+ nil
32
+ end
33
+ end
34
+
35
+ # Signs a transaction in the serialized transaction format using private keys.
36
+ # @param [String] tx The serialized format transaction.
37
+ # @return [Bitcoin::Protocol::Tx] The signed transaction.
38
+ def sign_transaction(tx)
39
+ signed_tx = request('signrawtransaction', tx)
40
+ raise OpenAssets::Error, 'Could not sign the transaction.' unless signed_tx['complete']
41
+ Bitcoin::Protocol::Tx.new(signed_tx['hex'].htb)
42
+ end
43
+
44
+ # Validates a transaction and broadcasts it to the peer-to-peer network.
45
+ # @param [String] tx The serialized format transaction.
46
+ # @return [String] The TXID or error message.
47
+ def send_transaction(tx)
48
+ request('sendrawtransaction', tx)
49
+ end
50
+
51
+ private
52
+ # Convert decode tx string to Bitcion::Protocol::Tx
53
+ def decode_tx_to_btc_tx(tx)
54
+ hash = {
55
+ 'version' => tx['version'],
56
+ 'lock_time' => tx['locktime'],
57
+ 'hex' => tx['hex'],
58
+ 'txid' => tx['txid'],
59
+ 'blockhash' => tx['blockhash'],
60
+ 'confirmations' => tx['confirmations'],
61
+ 'time' => tx['time'],
62
+ 'blocktime' => tx['blocktime'],
63
+ 'in' => tx['vin'].map{|input|
64
+ {'output_index' => input['vout'], 'previous_transaction_hash' => input['txid'], 'coinbase' => input['coinbase'],
65
+ 'scriptSig' => input['scriptSig']['asm'], 'sequence' => input['sequence']}},
66
+ 'out' => tx['vout'].map{|out|
67
+ {'amount' => out['value'], 'scriptPubKey' => out['scriptPubKey']['asm']}
68
+ }
69
+ }
70
+ Bitcoin::Protocol::Tx.from_hash(hash)
71
+ end
72
+
73
+ private
74
+ def server_url
75
+ url = "#{@config[:schema]}://"
76
+ url.concat "#{@config[:user]}:#{@config[:password]}@"
77
+ url.concat "#{@config[:host]}:#{@config[:port]}"
78
+ url
79
+ end
80
+
81
+ def request(command, *params)
82
+ data = {
83
+ :method => command,
84
+ :params => params,
85
+ :id => 'jsonrpc'
86
+ }
87
+ RestClient.post(server_url, data.to_json, content_type: :json) do |respdata, request, result|
88
+ response = JSON.parse(respdata)
89
+ raise ApiError, response['error'] if response['error']
90
+ response['result']
91
+ end
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,22 @@
1
+ module OpenAssets
2
+ module Provider
3
+
4
+ # The base class providing access to the Blockchain.
5
+ class BlockChainProviderBase
6
+
7
+ def list_unspent(addresses = [], min = 1 , max = 9999999)
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def get_transaction(transaction_hash, verbose = 0)
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def sign_transaction(tx)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ module OpenAssets
2
+ module Provider
3
+ autoload :BlockChainProviderBase, 'openassets/provider/block_chain_provider_base'
4
+ autoload :BitcoinCoreProvider, 'openassets/provider/bitcoin_core_provider'
5
+ autoload :ApiError, 'openassets/provider/api_error'
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ # The value of an output would be too small, and the output would be considered as dust.
5
+ class DustOutputError < TransactionBuildError
6
+
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ # An insufficient amount of bitcoins is available.
5
+ class InsufficientFundsError < TransactionBuildError
6
+
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ # The combination of a transaction hash and an index n into its vout
5
+ class OutPoint
6
+
7
+ attr_accessor :hash
8
+ attr_accessor :index
9
+
10
+ # @param [String] hash: 32 bytes transaction hash in vout.
11
+ # @param [Integer] index: index in vout.
12
+ def initialize(hash, index)
13
+ raise ArgumentError, 'hash must be exactly 32 bytes.' unless [hash].pack("H*").bytesize == 32
14
+ raise ArgumentError, 'index must be in range 0x0 to 0xffffffff.' unless index.between?(0x0, 0xffffffff)
15
+ @hash = hash
16
+ @index = index
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ # A transaction output with information about the asset ID and asset quantity associated to it.
5
+ class SpendableOutput
6
+
7
+ # An object that can be used to locate the output.
8
+ attr_accessor :out_point
9
+ # The actual output object.
10
+ attr_accessor :output
11
+
12
+ attr_accessor :confirmations
13
+
14
+ # @param [OpenAssets::Transaction::OutPoint] out_point
15
+ # @param [OpenAssets::Protocol::TransactionOutput] output
16
+ def initialize(out_point, output)
17
+ @out_point = out_point
18
+ @output = output
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ class TransactionBuildError < StandardError
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,95 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ class TransactionBuilder
5
+ include OpenAssets::Util
6
+
7
+ # The minimum allowed output value.
8
+ attr_accessor :amount
9
+
10
+ def initialize(amount = 600)
11
+ @amount = amount
12
+ end
13
+
14
+ # issue asset.
15
+ # @param [TransferParameters] issue_spec The parameters of the issuance.
16
+ # @param [bytes] metadata The metadata to be embedded in the transaction.
17
+ # @param [Integer] fees The fees to include in the transaction.
18
+ # @return An unsigned transaction for issuing asset.
19
+ def issue_asset(issue_spec, metadata, fees)
20
+ inputs, total_amount =
21
+ TransactionBuilder.collect_uncolored_outputs(issue_spec.unspent_outputs, 2 * @amount + fees)
22
+ tx = Bitcoin::Protocol::Tx.new
23
+ inputs.each { |spendable|
24
+ script_sig = spendable.output.script.to_binary
25
+ tx_in = Bitcoin::Protocol::TxIn.from_hex_hash(spendable.out_point.hash, spendable.out_point.index)
26
+ tx_in.script_sig = script_sig
27
+ tx.add_in(tx_in)
28
+ }
29
+ issue_address = oa_address_to_address(issue_spec.to_script)
30
+ from_address = oa_address_to_address(issue_spec.change_script)
31
+ validate_address([issue_address, from_address])
32
+ tx.add_out(create_colored_output(issue_address))
33
+ tx.add_out(create_marker_output([issue_spec.amount], metadata))
34
+ tx.add_out(create_uncolored_output(from_address, total_amount - @amount - fees))
35
+ tx
36
+ end
37
+
38
+ def transfer_assets
39
+
40
+ end
41
+
42
+ def transfer_btc
43
+
44
+ end
45
+
46
+ # collect uncolored outputs in unspent outputs(contains colored output).
47
+ # @param [Array[OpenAssets::Transaction::SpendableOutput]] unspent_outputs The Array of available outputs.
48
+ # @param [Integer] amount The amount to collect.
49
+ # @return [Array] inputs, total_amount
50
+ def self.collect_uncolored_outputs(unspent_outputs, amount)
51
+ total_amount = 0
52
+ results = []
53
+ unspent_outputs.each do |output|
54
+ if output.output.asset_id.nil?
55
+ results << output
56
+ total_amount += output.output.value
57
+ end
58
+ return results, total_amount if total_amount >= amount
59
+ end
60
+ raise InsufficientFundsError
61
+ end
62
+
63
+ private
64
+ # create colored output.
65
+ # @param [String] address The Bitcoin address.
66
+ # @return [Bitcoin::Protocol::TxOut] colored output
67
+ def create_colored_output(address)
68
+ hash160 = Bitcoin.hash160_from_address(address)
69
+ Bitcoin::Protocol::TxOut.new(@amount,
70
+ Bitcoin::Script.new(Bitcoin::Script.to_hash160_script(hash160)).to_payload)
71
+ end
72
+
73
+ # create marker output.
74
+ # @param [Array] asset_quantities asset_quantity array.
75
+ # @param [String] metadata
76
+ # @return [Bitcoin::Protocol::TxOut] the marker output.
77
+ def create_marker_output(asset_quantities, metadata)
78
+ script = OpenAssets::Protocol::MarkerOutput.new(asset_quantities, metadata).build_script
79
+ Bitcoin::Protocol::TxOut.new(0, script.to_payload)
80
+ end
81
+
82
+ # create an uncolored output.
83
+ # @param [String] address: The Bitcoin address.
84
+ # @param [Integer] value: The satoshi value of the output.
85
+ # @return [Bitcoin::Protocol::TxOut] an uncolored output.
86
+ def create_uncolored_output(address, value)
87
+ raise DustOutputError if value < @amount
88
+ hash160 = Bitcoin.hash160_from_address(address)
89
+ Bitcoin::Protocol::TxOut.new(value, Bitcoin::Script.new(Bitcoin::Script.to_hash160_script(hash160)).to_payload)
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,27 @@
1
+ module OpenAssets
2
+ module Transaction
3
+
4
+ # The value object of a bitcoin or asset transfer.
5
+ class TransferParameters
6
+
7
+ attr_accessor :unspent_outputs
8
+ attr_accessor :amount
9
+ attr_accessor :change_script
10
+ attr_accessor :to_script
11
+
12
+ # initialize
13
+ # @param [Array[OpenAssets::Transaction::SpendableOutput]] unspent_outputs Array of the unspent outputs available for the transaction.
14
+ # @param [String] to_script the output script to which to send the assets or bitcoins.
15
+ # @param [String] change_script the output script to which to send any remaining change.
16
+ # @param [Integer] amount The asset quantity or amount of the satoshi sent in the transaction.
17
+ def initialize(unspent_outputs, to_script, change_script, amount)
18
+ @unspent_outputs = unspent_outputs
19
+ @to_script = to_script
20
+ @change_script = change_script
21
+ @amount = amount
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module OpenAssets
2
+ module Transaction
3
+ autoload :TransactionBuilder, 'openassets/transaction/transaction_builder'
4
+ autoload :TransferParameters, 'openassets/transaction/transfer_parameters'
5
+ autoload :SpendableOutput, 'openassets/transaction/spendable_output'
6
+ autoload :OutPoint, 'openassets/transaction/out_point'
7
+ autoload :TransactionBuildError, 'openassets/transaction/transaction_build_error'
8
+ autoload :InsufficientFundsError, 'openassets/transaction/insufficient_funds_error'
9
+ autoload :DustOutputError, 'openassets/transaction/dust_output_error'
10
+ end
11
+ end
@@ -0,0 +1,93 @@
1
+ module OpenAssets
2
+ module Util
3
+ extend ::Bitcoin::Util
4
+ include ::Bitcoin::Util
5
+
6
+ # namespace of Open Asset
7
+ OA_NAMESPACE = 19
8
+
9
+ # version byte for Open Assets Address
10
+ OA_VERSION_BYTE = 23
11
+
12
+ # convert bitcoin address to open assets address
13
+ # @param [String] btc_address The Bitcoin address.
14
+ # @return [String] The Open Assets Address.
15
+ def address_to_oa_address(btc_address)
16
+ btc_hex = decode_base58(btc_address)
17
+ btc_hex = '0' +btc_hex if btc_hex.size==47
18
+ address = btc_hex[0..-9] # bitcoin address without checksum
19
+ named_addr = OA_NAMESPACE.to_s(16) + address
20
+ oa_checksum = checksum(named_addr)
21
+ encode_base58(named_addr + oa_checksum)
22
+ end
23
+
24
+ # convert open assets address to bitcoin address
25
+ # @param [String] oa_address The Open Assets Address.
26
+ # @return [String] The Bitcoin address.
27
+ def oa_address_to_address(oa_address)
28
+ decode_address = decode_base58(oa_address)
29
+ btc_addr = decode_address[2..-9]
30
+ btc_checksum = checksum(btc_addr)
31
+ encode_base58(btc_addr + btc_checksum)
32
+ end
33
+
34
+ # generate asset ID from public key.
35
+ def generate_asset_id(pub_key)
36
+ pubkey_hash_to_asset_id(hash160(pub_key))
37
+ end
38
+
39
+ def pubkey_hash_to_asset_id(hash)
40
+ # gen P2PKH script hash
41
+ # P2PKH script = OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
42
+ # (76=OP_DUP, a9=OP_HASH160, 14=Bytes to push, 88=OP_EQUALVERIFY, ac=OP_CHECKSIG)
43
+ script = hash160(["76", "a9", "14", hash, "88", "ac"].join)
44
+ script = OA_VERSION_BYTE.to_s(16) + script # add version byte to script hash
45
+ encode_base58(script + checksum(script)) # add checksum & encode
46
+ end
47
+
48
+ # LEB128 encode
49
+ def encode_leb128(value)
50
+ d7=->n{(n>>7)==0 ? [n] : d7[n>>7]+[127 & n]}
51
+ msb=->a{a0=a[0].to_s(16);[(a[0]< 16 ? "0"+a0 : a0)]+a[1..-1].map{|x|(x|128).to_s(16)}}
52
+ leb128=->n{msb[d7[n]].reverse.join}
53
+ leb128[value]
54
+ end
55
+
56
+ # LEB128 decode
57
+ def decode_leb128(value)
58
+ mbs = to_bytes(value).map{|x|(x.to_i(16)>=128 ? x : x+"|")}.join.split('|')
59
+ num=->a{(a.size==1 ? a[0] : (num[a[0..-2]]<<7)|a[-1])}
60
+ r7=->n{to_bytes(n).map{|x|(x.to_i(16))&127}}
61
+ mbs.map{|x|num[r7[x].reverse]}
62
+ end
63
+
64
+ def to_bytes(string)
65
+ string.each_char.each_slice(2).map{|v|v.join}
66
+ end
67
+
68
+ # Convert satoshi to coin.
69
+ # @param [Integer] satoshi The amount of satoshi unit.
70
+ # @return [String] The amount of coin.
71
+ def satoshi_to_coin(satoshi)
72
+ "%.8f" % (satoshi / 100000000.0)
73
+ end
74
+
75
+ # Get address from script.
76
+ # @param [Bitcoin::Script] script The output script.
77
+ # @return [String] The Bitcoin address. if the script dose not contains address, return nil.
78
+ def script_to_address(script)
79
+ return script.get_pubkey_address if script.is_pubkey?
80
+ return script.get_hash160_address if script.is_hash160?
81
+ return script.get_multisig_addresses if script.is_multisig?
82
+ return script.get_p2sh_address if script.is_p2sh?
83
+ nil
84
+ end
85
+
86
+ # validate bitcoin address
87
+ def validate_address(addresses)
88
+ addresses.each{|a|
89
+ raise ArgumentError, "#{a} is invalid bitcoin address. " unless valid_address?(a)
90
+ }
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,3 @@
1
+ module OpenAssets
2
+ VERSION = "0.1.1"
3
+ end
data/lib/openassets.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'bitcoin'
2
+ module OpenAssets
3
+
4
+ autoload :Protocol, 'openassets/protocol'
5
+ autoload :Transaction, 'openassets/transaction'
6
+ autoload :VERSION, 'openassets/version'
7
+ autoload :Util, 'openassets/util'
8
+ autoload :Api, 'openassets/api'
9
+ autoload :Provider, 'openassets/provider'
10
+ autoload :Error, 'openassets/error'
11
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'openassets/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "openassets-ruby"
8
+ spec.version = OpenAssets::VERSION
9
+ spec.authors = ["azuchi"]
10
+ spec.email = ["azuchi@haw.co.jp"]
11
+
12
+ spec.summary = %q{The implementation of the Open Assets Protocol for Ruby.}
13
+ spec.description = %q{The implementation of the Open Assets Protocol for Ruby.}
14
+ spec.homepage = "https://github.com/haw-itn/openassets-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+ spec.add_runtime_dependency "bitcoin-ruby", "~> 0.0.7"
22
+ spec.add_runtime_dependency "ffi", "~>1.9.8"
23
+ spec.add_runtime_dependency "rest-client", "~>1.8.0"
24
+ spec.add_development_dependency "bundler", "~> 1.9"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec"
27
+
28
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openassets-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - azuchi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bitcoin-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: ffi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.9.8
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.9.8
41
+ - !ruby/object:Gem::Dependency
42
+ name: rest-client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.8.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: The implementation of the Open Assets Protocol for Ruby.
98
+ email:
99
+ - azuchi@haw.co.jp
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".ruby-gemset"
107
+ - ".ruby-version"
108
+ - CODE_OF_CONDUCT.md
109
+ - Gemfile
110
+ - LICENSE
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/setup
116
+ - lib/openassets.rb
117
+ - lib/openassets/api.rb
118
+ - lib/openassets/error.rb
119
+ - lib/openassets/protocol.rb
120
+ - lib/openassets/protocol/marker_output.rb
121
+ - lib/openassets/protocol/output_type.rb
122
+ - lib/openassets/protocol/transaction_output.rb
123
+ - lib/openassets/provider.rb
124
+ - lib/openassets/provider/api_error.rb
125
+ - lib/openassets/provider/bitcoin_core_provider.rb
126
+ - lib/openassets/provider/block_chain_provider_base.rb
127
+ - lib/openassets/transaction.rb
128
+ - lib/openassets/transaction/dust_output_error.rb
129
+ - lib/openassets/transaction/insufficient_funds_error.rb
130
+ - lib/openassets/transaction/out_point.rb
131
+ - lib/openassets/transaction/spendable_output.rb
132
+ - lib/openassets/transaction/transaction_build_error.rb
133
+ - lib/openassets/transaction/transaction_builder.rb
134
+ - lib/openassets/transaction/transfer_parameters.rb
135
+ - lib/openassets/util.rb
136
+ - lib/openassets/version.rb
137
+ - openassets-ruby.gemspec
138
+ homepage: https://github.com/haw-itn/openassets-ruby
139
+ licenses:
140
+ - MIT
141
+ metadata: {}
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 2.4.6
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: The implementation of the Open Assets Protocol for Ruby.
162
+ test_files: []