openassets 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +11 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +23 -0
  10. data/README.md +423 -0
  11. data/Rakefile +5 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/exe/openassets +66 -0
  15. data/lib/openassets.rb +23 -0
  16. data/lib/openassets/api.rb +430 -0
  17. data/lib/openassets/cache.rb +8 -0
  18. data/lib/openassets/cache/output_cache.rb +43 -0
  19. data/lib/openassets/cache/sqlite_base.rb +26 -0
  20. data/lib/openassets/cache/ssl_certificate_cache.rb +44 -0
  21. data/lib/openassets/cache/transaction_cache.rb +33 -0
  22. data/lib/openassets/error.rb +5 -0
  23. data/lib/openassets/medhod_filter.rb +55 -0
  24. data/lib/openassets/protocol.rb +10 -0
  25. data/lib/openassets/protocol/asset_definition.rb +118 -0
  26. data/lib/openassets/protocol/asset_definition_loader.rb +45 -0
  27. data/lib/openassets/protocol/http_asset_definition_loader.rb +27 -0
  28. data/lib/openassets/protocol/marker_output.rb +137 -0
  29. data/lib/openassets/protocol/output_type.rb +27 -0
  30. data/lib/openassets/protocol/transaction_output.rb +136 -0
  31. data/lib/openassets/provider.rb +7 -0
  32. data/lib/openassets/provider/api_error.rb +9 -0
  33. data/lib/openassets/provider/bitcoin_core_provider.rb +103 -0
  34. data/lib/openassets/provider/block_chain_provider_base.rb +22 -0
  35. data/lib/openassets/send_asset_param.rb +17 -0
  36. data/lib/openassets/send_bitcoin_param.rb +13 -0
  37. data/lib/openassets/transaction.rb +11 -0
  38. data/lib/openassets/transaction/dust_output_error.rb +10 -0
  39. data/lib/openassets/transaction/insufficient_asset_quantity_error.rb +7 -0
  40. data/lib/openassets/transaction/insufficient_funds_error.rb +10 -0
  41. data/lib/openassets/transaction/spendable_output.rb +39 -0
  42. data/lib/openassets/transaction/transaction_build_error.rb +9 -0
  43. data/lib/openassets/transaction/transaction_builder.rb +312 -0
  44. data/lib/openassets/transaction/transfer_parameters.rb +41 -0
  45. data/lib/openassets/util.rb +165 -0
  46. data/lib/openassets/version.rb +3 -0
  47. data/openassets.gemspec +33 -0
  48. metadata +244 -0
@@ -0,0 +1,41 @@
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
+ attr_accessor :output_qty
12
+
13
+ # initialize
14
+ # @param [Array[OpenAssets::Transaction::SpendableOutput]] unspent_outputs Array of the unspent outputs available for the transaction.
15
+ # @param [String] to_script the output script to which to send the assets or bitcoins.
16
+ # @param [String] change_script the output script to which to send any remaining change.
17
+ # @param [Integer] amount The asset quantity or amount of the satoshi sent in the transaction.
18
+ def initialize(unspent_outputs, to_script, change_script, amount, output_qty = 1)
19
+ @unspent_outputs = unspent_outputs
20
+ @to_script = to_script
21
+ @change_script = change_script
22
+ @amount = amount
23
+ @output_qty = output_qty
24
+ end
25
+
26
+ def split_output_amount
27
+ split_amounts = []
28
+ output_qty.times{|index|
29
+ if index == output_qty - 1
30
+ value = amount / output_qty + amount % output_qty
31
+ else
32
+ value = amount / output_qty
33
+ end
34
+ split_amounts << value
35
+ }
36
+ split_amounts
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,165 @@
1
+ require 'bigdecimal'
2
+ module OpenAssets
3
+ module Util
4
+ extend ::Bitcoin::Util
5
+ include ::Bitcoin::Util
6
+
7
+ # namespace of Open Asset
8
+ OA_NAMESPACE = 19
9
+
10
+ # version byte for Open Assets Address
11
+ OA_VERSION_BYTE = 23
12
+ OA_VERSION_BYTE_TESTNET = 115
13
+
14
+ # convert bitcoin address to open assets address
15
+ # @param [String] btc_address The Bitcoin address.
16
+ # @return [String] The Open Assets Address.
17
+ def address_to_oa_address(btc_address)
18
+ begin
19
+ btc_hex = Bitcoin::Base58.decode(btc_address)
20
+ btc_hex = '0' +btc_hex if btc_hex.size==47
21
+ address = btc_hex[0..-9] # bitcoin address without checksum
22
+ named_addr = OA_NAMESPACE.to_s(16) + address
23
+ oa_checksum = Bitcoin.calc_checksum(named_addr)
24
+ Bitcoin::Base58.encode(named_addr + oa_checksum)
25
+ rescue ArgumentError
26
+ nil # bech32 format fails to decode. TODO define OA address for segwit
27
+ end
28
+ end
29
+
30
+ # convert open assets address to bitcoin address
31
+ # @param [String] oa_address The Open Assets Address.
32
+ # @return [String] The Bitcoin address.
33
+ def oa_address_to_address(oa_address)
34
+ decode_address = Bitcoin::Base58.decode(oa_address)
35
+ btc_addr = decode_address[2..-9]
36
+ btc_checksum = Bitcoin.calc_checksum(btc_addr)
37
+ Bitcoin::Base58.encode(btc_addr + btc_checksum)
38
+ end
39
+
40
+ # generate asset ID from public key.
41
+ def generate_asset_id(pub_key)
42
+ pubkey_hash_to_asset_id(hash160(pub_key))
43
+ end
44
+
45
+ def pubkey_hash_to_asset_id(pubkey_hash)
46
+ # gen P2PKH script hash
47
+ # P2PKH script = OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
48
+ # (76=OP_DUP, a9=OP_HASH160, 14=Bytes to push, 88=OP_EQUALVERIFY, ac=OP_CHECKSIG)
49
+ hash_to_asset_id(hash160(["76", "a9", "14", pubkey_hash, "88", "ac"].join))
50
+ end
51
+
52
+ def script_to_asset_id(script)
53
+ hash_to_asset_id(hash160(script))
54
+ end
55
+
56
+ def hash_to_asset_id(hash)
57
+ hash = oa_version_byte.to_s(16) + hash # add version byte to script hash
58
+ Bitcoin::Base58.encode(hash + Bitcoin.calc_checksum(hash)) # add checksum & encode
59
+ end
60
+
61
+ # LEB128 encode
62
+ def encode_leb128(value)
63
+ LEB128.encode_unsigned(value).read.bth
64
+ end
65
+
66
+ # LEB128 decode
67
+ def decode_leb128(value)
68
+ results = []
69
+ sio = StringIO.new
70
+ value.htb.each_byte{|b|
71
+ sio.putc(b)
72
+ if b < 128
73
+ results << LEB128.decode_unsigned(sio)
74
+ sio = StringIO.new
75
+ end
76
+ }
77
+ results
78
+ end
79
+
80
+ def to_bytes(string)
81
+ string.each_char.each_slice(2).map{|v|v.join}
82
+ end
83
+
84
+ # Convert satoshi to coin.
85
+ # @param [Integer] satoshi The amount of satoshi unit.
86
+ # @return [String] The amount of coin.
87
+ def satoshi_to_coin(satoshi)
88
+ "%.8f" % (satoshi / 100000000.0)
89
+ end
90
+
91
+ # Convert coin unit to satoshi.
92
+ # @param [String] coin The amount of bitcoin
93
+ # @return [String] The amount of satoshi.
94
+ def coin_to_satoshi(coin)
95
+ BigDecimal(coin) * BigDecimal(100000000)
96
+ end
97
+
98
+ # validate bitcoin address
99
+ def validate_address(addresses)
100
+ addresses.each{|a|
101
+ raise ArgumentError, "#{a} is invalid bitcoin address. " unless Bitcoin.valid_address?(a)
102
+ }
103
+ end
104
+
105
+ # validate asset ID
106
+ def valid_asset_id?(asset_id)
107
+ return false if asset_id.nil? || asset_id.length != 34
108
+ decoded = Bitcoin::Base58.decode(asset_id)
109
+ return false if decoded[0,2].to_i(16) != oa_version_byte
110
+ p2pkh_script_hash = decoded[2..-9]
111
+ p2pkh_script_hash.htb.bytesize == 20
112
+ end
113
+
114
+ # read variable integer
115
+ # @param [String] data reading data
116
+ # @param [Integer] offset the position when reading from data.
117
+ # @return [[Integer, Integer]] decoded integer value and the reading byte length.
118
+ # https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
119
+ def read_var_integer(data, offset = 0)
120
+ raise ArgumentError, "data is nil." unless data
121
+ packed = [data].pack('H*')
122
+ return [nil, 0] if packed.bytesize < 1+offset
123
+ bytes = packed.bytes[offset..(offset + 9)] # 9 is variable integer max storage length.
124
+ first_byte = bytes[0]
125
+ if first_byte < 0xfd
126
+ [first_byte, offset + 1]
127
+ elsif first_byte == 0xfd
128
+ [calc_var_integer_val(bytes[1..2]), offset + 3]
129
+ elsif first_byte == 0xfe
130
+ [calc_var_integer_val(bytes[1..4]), offset + 5]
131
+ elsif first_byte == 0xff
132
+ [calc_var_integer_val(bytes[1..8]), offset + 9]
133
+ end
134
+ end
135
+
136
+ # read leb128 value
137
+ # @param [String] data reading data
138
+ # @param [Integer] offset start position when reading from data.
139
+ # @return [[Integer, Integer]] decoded integer value and the reading byte length.
140
+ def read_leb128(data, offset = 0)
141
+ bytes = [data].pack('H*').bytes
142
+ result = 0
143
+ shift = 0
144
+ while true
145
+ return [nil, offset] if bytes.length < 1 + offset
146
+ byte = bytes[offset..(offset + 1)][0]
147
+ result |= (byte & 0x7f) << shift
148
+ break if byte & 0x80 == 0
149
+ shift += 7
150
+ offset += 1
151
+ end
152
+ [result, offset + 1]
153
+ end
154
+
155
+ private
156
+ def oa_version_byte
157
+ Bitcoin.chain_params.mainnet? ? OA_VERSION_BYTE : OA_VERSION_BYTE_TESTNET
158
+ end
159
+
160
+ def calc_var_integer_val(byte_array)
161
+ byte_array.each_with_index.inject(0){|sum, pair| pair[1] == 0 ? pair[0] : sum + pair[0]*(256**pair[1])}
162
+ end
163
+
164
+ end
165
+ end
@@ -0,0 +1,3 @@
1
+ module OpenAssets
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,33 @@
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"
8
+ spec.version = OpenAssets::VERSION
9
+ spec.authors = ["azuchi"]
10
+ spec.email = ["azuchi@chaintope.com"]
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/chaintope/openassetsrb"
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 "bitcoinrb", ">= 0.3.2"
22
+ spec.add_runtime_dependency "ffi", "~>1.9.8"
23
+ spec.add_runtime_dependency "rest-client", "2.0.2"
24
+ spec.add_runtime_dependency "httpclient"
25
+ spec.add_runtime_dependency "sqlite3"
26
+ spec.add_runtime_dependency "leb128", '~> 1.0.0'
27
+ spec.add_development_dependency "bundler"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "timecop"
31
+ spec.add_development_dependency "travis"
32
+
33
+ end
metadata ADDED
@@ -0,0 +1,244 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openassets
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - azuchi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-12-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bitcoinrb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.2
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: 2.0.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.0.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: httpclient
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: leb128
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.0.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: timecop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: travis
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: The implementation of the Open Assets Protocol for Ruby.
168
+ email:
169
+ - azuchi@chaintope.com
170
+ executables:
171
+ - openassets
172
+ extensions: []
173
+ extra_rdoc_files: []
174
+ files:
175
+ - ".gitignore"
176
+ - ".rspec"
177
+ - ".ruby-gemset"
178
+ - ".ruby-version"
179
+ - ".travis.yml"
180
+ - CODE_OF_CONDUCT.md
181
+ - Gemfile
182
+ - LICENSE
183
+ - README.md
184
+ - Rakefile
185
+ - bin/console
186
+ - bin/setup
187
+ - exe/openassets
188
+ - lib/openassets.rb
189
+ - lib/openassets/api.rb
190
+ - lib/openassets/cache.rb
191
+ - lib/openassets/cache/output_cache.rb
192
+ - lib/openassets/cache/sqlite_base.rb
193
+ - lib/openassets/cache/ssl_certificate_cache.rb
194
+ - lib/openassets/cache/transaction_cache.rb
195
+ - lib/openassets/error.rb
196
+ - lib/openassets/medhod_filter.rb
197
+ - lib/openassets/protocol.rb
198
+ - lib/openassets/protocol/asset_definition.rb
199
+ - lib/openassets/protocol/asset_definition_loader.rb
200
+ - lib/openassets/protocol/http_asset_definition_loader.rb
201
+ - lib/openassets/protocol/marker_output.rb
202
+ - lib/openassets/protocol/output_type.rb
203
+ - lib/openassets/protocol/transaction_output.rb
204
+ - lib/openassets/provider.rb
205
+ - lib/openassets/provider/api_error.rb
206
+ - lib/openassets/provider/bitcoin_core_provider.rb
207
+ - lib/openassets/provider/block_chain_provider_base.rb
208
+ - lib/openassets/send_asset_param.rb
209
+ - lib/openassets/send_bitcoin_param.rb
210
+ - lib/openassets/transaction.rb
211
+ - lib/openassets/transaction/dust_output_error.rb
212
+ - lib/openassets/transaction/insufficient_asset_quantity_error.rb
213
+ - lib/openassets/transaction/insufficient_funds_error.rb
214
+ - lib/openassets/transaction/spendable_output.rb
215
+ - lib/openassets/transaction/transaction_build_error.rb
216
+ - lib/openassets/transaction/transaction_builder.rb
217
+ - lib/openassets/transaction/transfer_parameters.rb
218
+ - lib/openassets/util.rb
219
+ - lib/openassets/version.rb
220
+ - openassets.gemspec
221
+ homepage: https://github.com/chaintope/openassetsrb
222
+ licenses:
223
+ - MIT
224
+ metadata: {}
225
+ post_install_message:
226
+ rdoc_options: []
227
+ require_paths:
228
+ - lib
229
+ required_ruby_version: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - ">="
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ required_rubygems_version: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - ">="
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
239
+ requirements: []
240
+ rubygems_version: 3.0.3
241
+ signing_key:
242
+ specification_version: 4
243
+ summary: The implementation of the Open Assets Protocol for Ruby.
244
+ test_files: []