monacoin-openassets-ruby 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) 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 +420 -0
  11. data/Rakefile +5 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/exe/monacoin-openassets +66 -0
  15. data/lib/monacoin-openassets.rb +23 -0
  16. data/lib/openassets/api.rb +423 -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 +43 -0
  27. data/lib/openassets/protocol/http_asset_definition_loader.rb +27 -0
  28. data/lib/openassets/protocol/marker_output.rb +128 -0
  29. data/lib/openassets/protocol/output_type.rb +27 -0
  30. data/lib/openassets/protocol/transaction_output.rb +157 -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 +123 -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 +12 -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/out_point.rb +22 -0
  42. data/lib/openassets/transaction/spendable_output.rb +38 -0
  43. data/lib/openassets/transaction/transaction_build_error.rb +9 -0
  44. data/lib/openassets/transaction/transaction_builder.rb +319 -0
  45. data/lib/openassets/transaction/transfer_parameters.rb +41 -0
  46. data/lib/openassets/util.rb +173 -0
  47. data/lib/openassets/version.rb +3 -0
  48. data/openassets-ruby.gemspec +38 -0
  49. metadata +246 -0
@@ -0,0 +1,27 @@
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
+ def self.output_type_label(type)
16
+ case type
17
+ when UNCOLORED then 'uncolored'
18
+ when MARKER_OUTPUT then 'marker'
19
+ when ISSUANCE then 'issuance'
20
+ when TRANSFER then 'transfer'
21
+ else 'uncolored'
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,157 @@
1
+ module OpenAssets
2
+ module Protocol
3
+
4
+ # Represents a transaction output and its asset ID and asset quantity.
5
+ class TransactionOutput
6
+
7
+ include OpenAssets::Util
8
+
9
+ attr_accessor :value
10
+ attr_accessor :script
11
+ attr_accessor :asset_id
12
+ attr_accessor :asset_quantity
13
+ attr_accessor :output_type
14
+
15
+ attr_accessor :account
16
+ attr_accessor :metadata
17
+ attr_accessor :asset_definition_url
18
+ attr_accessor :asset_definition
19
+
20
+
21
+ # @param [Integer] value The satoshi value of the output.
22
+ # @param [Bitcoin::Script] script The script controlling redemption of the output.
23
+ # @param [String] asset_id The asset ID of the output.
24
+ # @param [Integer] asset_quantity The asset quantity of the output.
25
+ # @param [OpenAssets::Transaction::OutPutType] output_type The type of the output.
26
+ def initialize(value, script, asset_id = nil, asset_quantity = 0, output_type = OutputType::UNCOLORED, metadata = '')
27
+ raise ArgumentError, "invalid output_type : #{output_type}" unless OutputType.all.include?(output_type)
28
+ raise ArgumentError, "invalid asset_quantity. asset_quantity should be unsignd integer. " unless asset_quantity.between?(0, MarkerOutput::MAX_ASSET_QUANTITY)
29
+ @value = value
30
+ @script = script
31
+ @asset_id = asset_id
32
+ @asset_quantity = asset_quantity
33
+ @output_type = output_type
34
+ @metadata = metadata
35
+ load_asset_definition_url
36
+ end
37
+
38
+ # calculate asset amount.
39
+ # asset amount is the value obtained by converting the asset quantity to the unit of divisibility that are defined in the Asset definition file.
40
+ def asset_amount
41
+ d = divisibility
42
+ d > 0 ? (@asset_quantity.to_f / (10 ** d)).to_f : @asset_quantity
43
+ end
44
+
45
+ # get divisibility defined by asset definition file.
46
+ def divisibility
47
+ return 0 if !valid_asset_definition? || @asset_definition.divisibility.nil?
48
+ @asset_definition.divisibility
49
+ end
50
+
51
+ # Verify proof of authenticity.
52
+ def proof_of_authenticity
53
+ valid_asset_definition? ? @asset_definition.proof_of_authenticity : false
54
+ end
55
+
56
+ # convert to hash object.
57
+ def to_hash
58
+ {
59
+ 'address' => address,
60
+ 'oa_address' => oa_address,
61
+ 'script' => @script.to_payload.bth,
62
+ 'script_type' => script_type,
63
+ 'amount' => satoshi_to_coin(@value),
64
+ 'asset_id' => @asset_id,
65
+ 'asset_quantity' => @asset_quantity.to_s,
66
+ 'asset_amount' => asset_amount.to_s,
67
+ 'account' => @account,
68
+ 'asset_definition_url' => @asset_definition_url,
69
+ 'proof_of_authenticity' => proof_of_authenticity,
70
+ 'output_type' => OpenAssets::Protocol::OutputType.output_type_label(@output_type)
71
+ }
72
+ end
73
+
74
+ def address
75
+ if @script.is_multisig?
76
+ @script.get_multisig_addresses.each do |address|
77
+ return nil if address.nil?
78
+ end
79
+ end
80
+ script_to_address(@script)
81
+ end
82
+
83
+ def oa_address
84
+ a = address
85
+ return nil if a.nil?
86
+ if a.is_a?(Array)
87
+ a.map{|btc_address| address_to_oa_address(btc_address)}
88
+ else
89
+ address_to_oa_address(a)
90
+ end
91
+ end
92
+
93
+ # get pubkey script type
94
+ def script_type
95
+ case @script.type
96
+ when :hash160
97
+ 'pubkeyhash'
98
+ when :pubkey
99
+ 'pubkey'
100
+ when :multisig
101
+ 'multisig'
102
+ when :p2sh
103
+ 'scripthash'
104
+ when :op_return
105
+ 'nulldata'
106
+ when :witness_v0_keyhash
107
+ 'witness_v0_keyhash'
108
+ when :witness_v0_scripthash
109
+ 'witness_v0_scripthash'
110
+ when :unknown
111
+ 'nonstandard'
112
+ else
113
+ 'nonstandard'
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ @@definition_cache = {}
120
+
121
+ # get Asset definition url that is included metadata.
122
+ def load_asset_definition_url
123
+ @asset_definition_url = ''
124
+ return if @metadata.nil? || @metadata.length == 0
125
+ if @metadata.start_with?('u=')
126
+ @asset_definition = load_asset_definition(metadata_url)
127
+ if valid_asset_definition?
128
+ @asset_definition_url = metadata_url
129
+ else
130
+ @asset_definition_url = "The asset definition is invalid. #{metadata_url}"
131
+ end
132
+ else
133
+ @asset_definition_url = 'Invalid metadata format.'
134
+ end
135
+ end
136
+
137
+ def metadata_url
138
+ unless @metadata.nil?
139
+ @metadata.slice(2..-1)
140
+ end
141
+ end
142
+
143
+ def valid_asset_definition?
144
+ !@asset_definition.nil? && @asset_definition.include_asset_id?(@asset_id)
145
+ end
146
+
147
+ def load_asset_definition(url)
148
+ unless @@definition_cache.has_key?(url)
149
+ loader = AssetDefinitionLoader.new(metadata_url)
150
+ @@definition_cache[url] = loader.load_definition
151
+ end
152
+ @@definition_cache[url]
153
+ end
154
+ end
155
+
156
+ end
157
+ 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,9 @@
1
+ module OpenAssets
2
+ module Provider
3
+
4
+ class ApiError < OpenAssets::Error
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,123 @@
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
+
14
+ commands = request(:help).split("\n").inject([]) do |memo_ary, line|
15
+ if !line.empty? && !line.start_with?('==')
16
+ memo_ary << line.split(' ').first.to_sym
17
+ end
18
+ memo_ary
19
+ end
20
+ BitcoinCoreProvider.class_eval do
21
+ commands.each do |command|
22
+ define_method(command) do |*params|
23
+ request(command, *params)
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ # Get an array of unspent transaction outputs belonging to this wallet.
30
+ # @param [Array] addresses If present, only outputs which pay an address in this array will be returned.
31
+ # @param [Integer] min The minimum number of confirmations the transaction containing an output must have in order to be returned. Default is 1.
32
+ # @param [Integer] max The maximum number of confirmations the transaction containing an output may have in order to be returned. Default is 9999999.
33
+ def list_unspent(addresses = [], min = 1 , max = 9999999)
34
+ listunspent(min, max, addresses)
35
+ end
36
+
37
+ # Get raw transaction.
38
+ # @param [String] transaction_hash The transaction hash.
39
+ # @param [String] verbose Whether to get the serialized or decoded transaction. 0: serialized transaction (Default). 1: decode transaction.
40
+ # @return [String] (if verbose=0)—the serialized transaction. (if verbose=1)—the decoded transaction. (if transaction not found)—nil.
41
+ def get_transaction(transaction_hash, verbose = 0)
42
+ begin
43
+ getrawtransaction(transaction_hash, verbose)
44
+ rescue OpenAssets::Provider::ApiError => e
45
+ nil
46
+ end
47
+ end
48
+
49
+ # Signs a transaction in the serialized transaction format using private keys.
50
+ # @param [String] tx The serialized format transaction.
51
+ # @return [Bitcoin::Protocol::Tx] The signed transaction.
52
+ def sign_transaction(tx)
53
+ signed_tx = signrawtransaction(tx)
54
+ raise OpenAssets::Error, 'Could not sign the transaction.' unless signed_tx['complete']
55
+ Bitcoin::Protocol::Tx.new(signed_tx['hex'].htb)
56
+ end
57
+
58
+ # Validates a transaction and broadcasts it to the peer-to-peer network.
59
+ # @param [String] tx The serialized format transaction.
60
+ # @return [String] The TXID or error message.
61
+ def send_transaction(tx)
62
+ sendrawtransaction(tx)
63
+ end
64
+
65
+ # Adds an address or pubkey script to the wallet without the associated private key, allowing you to watch for transactions affecting that address or pubkey script without being able to spend any of its outputs.
66
+ # @param [String] address Either a P2PKH or P2SH address encoded in base58check, or a pubkey script encoded as hex.
67
+ def import_address(address)
68
+ importaddress(address)
69
+ end
70
+
71
+ private
72
+ # Convert decode tx string to Bitcion::Protocol::Tx
73
+ def decode_tx_to_btc_tx(tx)
74
+ hash = {
75
+ 'version' => tx['version'],
76
+ 'lock_time' => tx['locktime'],
77
+ 'hex' => tx['hex'],
78
+ 'txid' => tx['txid'],
79
+ 'blockhash' => tx['blockhash'],
80
+ 'confirmations' => tx['confirmations'],
81
+ 'time' => tx['time'],
82
+ 'blocktime' => tx['blocktime'],
83
+ 'in' => tx['vin'].map{|input|
84
+ {'output_index' => input['vout'], 'previous_transaction_hash' => input['txid'], 'coinbase' => input['coinbase'],
85
+ 'scriptSig' => input['scriptSig']['asm'], 'sequence' => input['sequence']}},
86
+ 'out' => tx['vout'].map{|out|
87
+ {'amount' => out['value'], 'scriptPubKey' => out['scriptPubKey']['asm']}
88
+ }
89
+ }
90
+ Bitcoin::Protocol::Tx.from_hash(hash)
91
+ end
92
+
93
+ private
94
+ def server_url
95
+ url = "#{@config[:schema]}://"
96
+ url.concat "#{@config[:user]}:#{@config[:password]}@"
97
+ url.concat "#{@config[:host]}:#{@config[:port]}"
98
+ if !@config[:wallet].nil? && !@config[:wallet].empty?
99
+ url.concat "/wallet/#{@config[:wallet]}"
100
+ end
101
+ url
102
+ end
103
+
104
+ def request(command, *params)
105
+ data = {
106
+ :method => command,
107
+ :params => params,
108
+ :id => 'jsonrpc'
109
+ }
110
+ post(server_url, @config[:timeout], @config[:open_timeout], data.to_json, content_type: :json) do |respdata, request, result|
111
+ raise ApiError, result.message if !result.kind_of?(Net::HTTPSuccess) && respdata.empty?
112
+ response = JSON.parse(respdata.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8') })
113
+ raise ApiError, response['error'] if response['error']
114
+ response['result']
115
+ end
116
+ end
117
+
118
+ def post(url, timeout, open_timeout, payload, headers={}, &block)
119
+ RestClient::Request.execute(:method => :post, :url => url, :timeout => timeout, :open_timeout => open_timeout, :payload => payload, :headers => headers, &block)
120
+ end
121
+ end
122
+ end
123
+ 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,17 @@
1
+ module OpenAssets
2
+
3
+ class SendAssetParam
4
+ attr_accessor :asset_id
5
+ attr_accessor :amount
6
+ attr_accessor :to
7
+ attr_accessor :from
8
+
9
+ def initialize(asset_id, amount, to, from = nil)
10
+ @asset_id = asset_id
11
+ @amount = amount
12
+ @to = to
13
+ @from = from
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,13 @@
1
+ module OpenAssets
2
+
3
+ class SendBitcoinParam
4
+ attr_accessor :amount
5
+ attr_accessor :to
6
+
7
+ def initialize(amount, to)
8
+ @amount = amount
9
+ @to = to
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,12 @@
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 :InsufficientAssetQuantityError, 'openassets/transaction/insufficient_asset_quantity_error'
10
+ autoload :DustOutputError, 'openassets/transaction/dust_output_error'
11
+ end
12
+ 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,7 @@
1
+ module OpenAssets
2
+ module Transaction
3
+ # An insufficient amount of assets is available.
4
+ class InsufficientAssetQuantityError < TransactionBuildError
5
+ end
6
+ end
7
+ 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