monacoin-openassets-ruby 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +23 -0
- data/README.md +420 -0
- data/Rakefile +5 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/monacoin-openassets +66 -0
- data/lib/monacoin-openassets.rb +23 -0
- data/lib/openassets/api.rb +423 -0
- data/lib/openassets/cache.rb +8 -0
- data/lib/openassets/cache/output_cache.rb +43 -0
- data/lib/openassets/cache/sqlite_base.rb +26 -0
- data/lib/openassets/cache/ssl_certificate_cache.rb +44 -0
- data/lib/openassets/cache/transaction_cache.rb +33 -0
- data/lib/openassets/error.rb +5 -0
- data/lib/openassets/medhod_filter.rb +55 -0
- data/lib/openassets/protocol.rb +10 -0
- data/lib/openassets/protocol/asset_definition.rb +118 -0
- data/lib/openassets/protocol/asset_definition_loader.rb +43 -0
- data/lib/openassets/protocol/http_asset_definition_loader.rb +27 -0
- data/lib/openassets/protocol/marker_output.rb +128 -0
- data/lib/openassets/protocol/output_type.rb +27 -0
- data/lib/openassets/protocol/transaction_output.rb +157 -0
- data/lib/openassets/provider.rb +7 -0
- data/lib/openassets/provider/api_error.rb +9 -0
- data/lib/openassets/provider/bitcoin_core_provider.rb +123 -0
- data/lib/openassets/provider/block_chain_provider_base.rb +22 -0
- data/lib/openassets/send_asset_param.rb +17 -0
- data/lib/openassets/send_bitcoin_param.rb +13 -0
- data/lib/openassets/transaction.rb +12 -0
- data/lib/openassets/transaction/dust_output_error.rb +10 -0
- data/lib/openassets/transaction/insufficient_asset_quantity_error.rb +7 -0
- data/lib/openassets/transaction/insufficient_funds_error.rb +10 -0
- data/lib/openassets/transaction/out_point.rb +22 -0
- data/lib/openassets/transaction/spendable_output.rb +38 -0
- data/lib/openassets/transaction/transaction_build_error.rb +9 -0
- data/lib/openassets/transaction/transaction_builder.rb +319 -0
- data/lib/openassets/transaction/transfer_parameters.rb +41 -0
- data/lib/openassets/util.rb +173 -0
- data/lib/openassets/version.rb +3 -0
- data/openassets-ruby.gemspec +38 -0
- 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,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,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,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
|