radiator 0.2.0a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +19 -0
- data/.gitignore +50 -0
- data/.travis.yml +12 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +86 -0
- data/LICENSE +41 -0
- data/README.md +362 -0
- data/Rakefile +39 -0
- data/lib/radiator/account_by_key_api.rb +7 -0
- data/lib/radiator/api.rb +168 -0
- data/lib/radiator/base_error.rb +17 -0
- data/lib/radiator/broadcast_operations.json +489 -0
- data/lib/radiator/chain_config.rb +29 -0
- data/lib/radiator/chain_stats_api.rb +15 -0
- data/lib/radiator/database_api.rb +4 -0
- data/lib/radiator/follow_api.rb +7 -0
- data/lib/radiator/logger.rb +18 -0
- data/lib/radiator/market_history_api.rb +19 -0
- data/lib/radiator/methods.json +488 -0
- data/lib/radiator/network_broadcast_api.rb +7 -0
- data/lib/radiator/operation.rb +75 -0
- data/lib/radiator/operation_ids.rb +80 -0
- data/lib/radiator/operation_types.rb +113 -0
- data/lib/radiator/stream.rb +260 -0
- data/lib/radiator/tag_api.rb +11 -0
- data/lib/radiator/transaction.rb +150 -0
- data/lib/radiator/type/amount.rb +36 -0
- data/lib/radiator/type/permission.rb +17 -0
- data/lib/radiator/type/point_in_time.rb +19 -0
- data/lib/radiator/type/public_key.rb +17 -0
- data/lib/radiator/type/u_int16.rb +19 -0
- data/lib/radiator/type/u_int32.rb +19 -0
- data/lib/radiator/utils.rb +62 -0
- data/lib/radiator/version.rb +3 -0
- data/lib/radiator.rb +29 -0
- data/radiator.gemspec +42 -0
- metadata +406 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
module Radiator
|
2
|
+
|
3
|
+
# See: https://github.com/steemit/steem-js/blob/766746adb5ded86380be982c844f4c269f7800ae/src/auth/serializer/src/operations.js
|
4
|
+
module OperationTypes
|
5
|
+
TYPES = {
|
6
|
+
transfer: {
|
7
|
+
amount: Type::Amount
|
8
|
+
},
|
9
|
+
transfer_to_vesting: {
|
10
|
+
amount: Type::Amount
|
11
|
+
},
|
12
|
+
withdraw_vesting: {
|
13
|
+
vesting_shares: Type::Amount
|
14
|
+
},
|
15
|
+
limit_order_create: {
|
16
|
+
orderid: Type::Uint32,
|
17
|
+
amount_to_sell: Type::Amount,
|
18
|
+
min_to_receive: Type::Amount,
|
19
|
+
expiration: Type::PointInTime
|
20
|
+
},
|
21
|
+
limit_order_cancel: {
|
22
|
+
orderid: Type::Uint32
|
23
|
+
},
|
24
|
+
convert: {
|
25
|
+
requestid: Type::Uint32,
|
26
|
+
amount: Type::Amount
|
27
|
+
},
|
28
|
+
account_create: {
|
29
|
+
fee: Type::Amount,
|
30
|
+
owner: Type::Permission,
|
31
|
+
active: Type::Permission,
|
32
|
+
posting: Type::Permission,
|
33
|
+
memo: Type::Permission
|
34
|
+
},
|
35
|
+
account_update: {
|
36
|
+
owner: Type::Permission,
|
37
|
+
active: Type::Permission,
|
38
|
+
posting: Type::Permission,
|
39
|
+
memo: Type::PublicKey
|
40
|
+
},
|
41
|
+
custom: {
|
42
|
+
id: Type::Uint16
|
43
|
+
},
|
44
|
+
comment_options: {
|
45
|
+
max_accepted_payout: Type::Amount
|
46
|
+
},
|
47
|
+
set_withdraw_vesting_route: {
|
48
|
+
percent: Type::Uint16
|
49
|
+
},
|
50
|
+
request_account_recovery: {
|
51
|
+
new_owner_Permission: Type::Permission
|
52
|
+
},
|
53
|
+
recover_account: {
|
54
|
+
new_owner_Permission: Type::Permission,
|
55
|
+
recent_owner_Permission: Type::Permission
|
56
|
+
},
|
57
|
+
escrow_transfer: {
|
58
|
+
sbd_amount: Type::Amount,
|
59
|
+
steem_amount: Type::Amount,
|
60
|
+
escrow_id: Type::Uint32,
|
61
|
+
fee: Type::Amount,
|
62
|
+
ratification_deadline: Type::PointInTime,
|
63
|
+
escrow_expiration: Type::PointInTime
|
64
|
+
},
|
65
|
+
escrow_dispute: {
|
66
|
+
escrow_id: Type::Uint32
|
67
|
+
},
|
68
|
+
escrow_release: {
|
69
|
+
escrow_id: Type::Uint32,
|
70
|
+
sbd_amount: Type::Amount,
|
71
|
+
steem_amount: Type::Amount
|
72
|
+
},
|
73
|
+
escrow_approve: {
|
74
|
+
escrow_id: Type::Uint32
|
75
|
+
},
|
76
|
+
transfer_to_savings: {
|
77
|
+
amount: Type::Amount
|
78
|
+
},
|
79
|
+
transfer_from_savings: {
|
80
|
+
request_id: Type::Uint32,
|
81
|
+
amount: Type::Amount
|
82
|
+
},
|
83
|
+
cancel_transfer_from_savings: {
|
84
|
+
request_id: Type::Uint32
|
85
|
+
},
|
86
|
+
reset_account: {
|
87
|
+
new_owner_Permission: Type::Amount
|
88
|
+
},
|
89
|
+
set_reset_account: {
|
90
|
+
reward_steem: Type::Amount,
|
91
|
+
reward_sbd: Type::Amount,
|
92
|
+
reward_vests: Type::Amount
|
93
|
+
},
|
94
|
+
claim_reward_balance: {
|
95
|
+
reward_steem: Type::Amount,
|
96
|
+
reward_sbd: Type::Amount,
|
97
|
+
reward_vests: Type::Amount
|
98
|
+
},
|
99
|
+
delegate_vesting_shares: {
|
100
|
+
vesting_shares: Type::Amount
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
def type(key, param, value)
|
105
|
+
return if value.nil?
|
106
|
+
|
107
|
+
t = TYPES[key] or return value
|
108
|
+
p = t[param] or return value
|
109
|
+
|
110
|
+
p.new(value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
module Radiator
|
2
|
+
# Radiator::Stream allows a live view of the STEEM blockchain.
|
3
|
+
#
|
4
|
+
# All values returned by `get_dynamic_global_properties` can be streamed.
|
5
|
+
#
|
6
|
+
# For example, if you want to know which witness is currently signing blocks,
|
7
|
+
# use the following:
|
8
|
+
#
|
9
|
+
# stream = Radiator::Stream.new
|
10
|
+
# stream.current_witness do |witness|
|
11
|
+
# puts witness
|
12
|
+
# end
|
13
|
+
class Stream < Api
|
14
|
+
INITIAL_TIMEOUT = 0.0200
|
15
|
+
MAX_TIMEOUT = 80
|
16
|
+
MAX_BLOCKS_PER_NODE = 100
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@api_options = options
|
20
|
+
@logger = @api_options[:logger] || Radiator.logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def api
|
24
|
+
@api ||= Api.new(@api_options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_names
|
28
|
+
@method_names ||= [
|
29
|
+
:head_block_number,
|
30
|
+
:head_block_id,
|
31
|
+
:time,
|
32
|
+
:current_witness,
|
33
|
+
:total_pow,
|
34
|
+
:num_pow_witnesses,
|
35
|
+
:virtual_supply,
|
36
|
+
:current_supply,
|
37
|
+
:confidential_supply,
|
38
|
+
:current_sbd_supply,
|
39
|
+
:confidential_sbd_supply,
|
40
|
+
:total_vesting_fund_steem,
|
41
|
+
:total_vesting_shares,
|
42
|
+
:total_reward_fund_steem,
|
43
|
+
:total_reward_shares2,
|
44
|
+
:total_activity_fund_steem,
|
45
|
+
:total_activity_fund_shares,
|
46
|
+
:sbd_interest_rate,
|
47
|
+
:average_block_size,
|
48
|
+
:maximum_block_size,
|
49
|
+
:current_aslot,
|
50
|
+
:recent_slots_filled,
|
51
|
+
:participation_count,
|
52
|
+
:last_irreversible_block_num,
|
53
|
+
:max_virtual_bandwidth,
|
54
|
+
:current_reserve_ratio,
|
55
|
+
:block_numbers,
|
56
|
+
:blocks
|
57
|
+
].freeze
|
58
|
+
end
|
59
|
+
|
60
|
+
def method_params(method)
|
61
|
+
case method
|
62
|
+
when :block_numbers then {head_block_number: nil}
|
63
|
+
when :blocks then {get_block: :head_block_number}
|
64
|
+
else; nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the latest operations from the blockchain.
|
69
|
+
#
|
70
|
+
# If symbol are passed, then only that operation is returned. Expected
|
71
|
+
# symbols are:
|
72
|
+
#
|
73
|
+
# transfer_to_vesting
|
74
|
+
# withdraw_vesting
|
75
|
+
# interest
|
76
|
+
# transfer
|
77
|
+
# liquidity_reward
|
78
|
+
# author_reward
|
79
|
+
# curation_reward
|
80
|
+
# transfer_to_savings
|
81
|
+
# transfer_from_savings
|
82
|
+
# cancel_transfer_from_savings
|
83
|
+
# escrow_transfer
|
84
|
+
# escrow_approve
|
85
|
+
# escrow_dispute
|
86
|
+
# escrow_release
|
87
|
+
# comment
|
88
|
+
# limit_order_create
|
89
|
+
# limit_order_cancel
|
90
|
+
# fill_convert_request
|
91
|
+
# fill_order
|
92
|
+
# vote
|
93
|
+
# account_witness_vote
|
94
|
+
# account_witness_proxy
|
95
|
+
# account_create
|
96
|
+
# account_update
|
97
|
+
# witness_update
|
98
|
+
# pow
|
99
|
+
# custom
|
100
|
+
#
|
101
|
+
# @param type [symbol || Array<symbol>] the type(s) of operation, optional.
|
102
|
+
# @param start starting block
|
103
|
+
# @param mode we have the choice between
|
104
|
+
# * :head the last block
|
105
|
+
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
106
|
+
# @param block the block to execute for each result, optional.
|
107
|
+
# @return [Hash]
|
108
|
+
def operations(type = nil, start = nil, mode = :irreversible, &block)
|
109
|
+
transactions(start, mode) do |transaction|
|
110
|
+
ops = transaction.operations.map do |t, op|
|
111
|
+
t = t.to_sym
|
112
|
+
if type == t
|
113
|
+
op
|
114
|
+
elsif type.nil? || [type].flatten.include?(t)
|
115
|
+
{t => op}
|
116
|
+
end
|
117
|
+
end.compact
|
118
|
+
|
119
|
+
next if ops.none?
|
120
|
+
|
121
|
+
return ops unless !!block
|
122
|
+
|
123
|
+
ops.each do |op|
|
124
|
+
yield op
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the latest transactions from the blockchain.
|
130
|
+
#
|
131
|
+
# @param start starting block
|
132
|
+
# @param mode we have the choice between
|
133
|
+
# * :head the last block
|
134
|
+
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
135
|
+
# @param block the block to execute for each result, optional.
|
136
|
+
# @return [Hash]
|
137
|
+
def transactions(start = nil, mode = :irreversible, &block)
|
138
|
+
blocks(start, mode) do |b|
|
139
|
+
next if (_transactions = b.transactions).nil?
|
140
|
+
return _transactions unless !!block
|
141
|
+
|
142
|
+
_transactions.each.each do |transaction|
|
143
|
+
yield transaction
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns the latest blocks from the blockchain.
|
149
|
+
#
|
150
|
+
# @param start starting block
|
151
|
+
# @param mode we have the choice between
|
152
|
+
# * :head the last block
|
153
|
+
# * :irreversible the block that is confirmed by 2/3 of all block producers and is thus irreversible!
|
154
|
+
# * :max_blocks_per_node the number of blocks to read before trying a new node
|
155
|
+
# @param block the block to execute for each result, optional.
|
156
|
+
# @return [Hash]
|
157
|
+
def blocks(start = nil, mode = :irreversible, max_blocks_per_node = MAX_BLOCKS_PER_NODE, &block)
|
158
|
+
counter = 0
|
159
|
+
|
160
|
+
if start.nil?
|
161
|
+
properties = api.get_dynamic_global_properties.result
|
162
|
+
start = case mode.to_sym
|
163
|
+
when :head then properties.head_block_number
|
164
|
+
when :irreversible then properties.last_irreversible_block_num
|
165
|
+
else; raise StreamError, '"mode" has to be "head" or "irreversible"'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
loop do
|
170
|
+
properties = api.get_dynamic_global_properties.result
|
171
|
+
|
172
|
+
head_block = case mode.to_sym
|
173
|
+
when :head then properties.head_block_number
|
174
|
+
when :irreversible then properties.last_irreversible_block_num
|
175
|
+
else; raise StreamError, '"mode" has to be "head" or "irreversible"'
|
176
|
+
end
|
177
|
+
|
178
|
+
[*(start..(head_block))].each do |n|
|
179
|
+
if (counter += 1) > max_blocks_per_node
|
180
|
+
shutdown
|
181
|
+
counter = 0
|
182
|
+
end
|
183
|
+
|
184
|
+
response = api.send(:get_block, n)
|
185
|
+
raise StreamError, JSON[response.error] if !!response.error
|
186
|
+
result = response.result
|
187
|
+
|
188
|
+
if !!block
|
189
|
+
yield result
|
190
|
+
else
|
191
|
+
return result
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
start = head_block + 1
|
196
|
+
sleep 3
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def method_missing(m, *args, &block)
|
201
|
+
super unless respond_to_missing?(m)
|
202
|
+
|
203
|
+
@latest_values ||= []
|
204
|
+
@latest_values.shift(5) if @latest_values.size > 20
|
205
|
+
loop do
|
206
|
+
value = if (n = method_params(m)).nil?
|
207
|
+
key_value = api.get_dynamic_global_properties.result[m]
|
208
|
+
else
|
209
|
+
key = n.keys.first
|
210
|
+
if !!n[key]
|
211
|
+
r = api.get_dynamic_global_properties.result
|
212
|
+
key_value = param = r[n[key]]
|
213
|
+
result = nil
|
214
|
+
loop do
|
215
|
+
response = api.send(key, param)
|
216
|
+
raise StreamError, JSON[response.error] if !!response.error
|
217
|
+
result = response.result
|
218
|
+
break if !!result
|
219
|
+
@logger.warn "#{key}: #{param} result missing, retrying with timeout: #{@timeout || INITIAL_TIMEOUT} seconds"
|
220
|
+
shutdown
|
221
|
+
sleep timeout
|
222
|
+
end
|
223
|
+
@timeout = INITIAL_TIMEOUT
|
224
|
+
result
|
225
|
+
else
|
226
|
+
key_value = api.get_dynamic_global_properties.result[key]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
unless @latest_values.include? key_value
|
230
|
+
@latest_values << key_value
|
231
|
+
if !!block
|
232
|
+
yield value
|
233
|
+
else
|
234
|
+
return value
|
235
|
+
end
|
236
|
+
end
|
237
|
+
sleep 0.0200
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def timeout
|
242
|
+
@timeout ||= INITIAL_TIMEOUT
|
243
|
+
@timeout *= 2
|
244
|
+
@timeout = INITIAL_TIMEOUT if @timeout > MAX_TIMEOUT
|
245
|
+
@timeout
|
246
|
+
end
|
247
|
+
|
248
|
+
# Stops the persistant http connections.
|
249
|
+
#
|
250
|
+
def shutdown
|
251
|
+
begin
|
252
|
+
@api.shutdown
|
253
|
+
rescue => e
|
254
|
+
@logger.warn("Unable to shut down: #{e}")
|
255
|
+
end
|
256
|
+
|
257
|
+
@api = nil
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'bitcoin'
|
2
|
+
require 'digest'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module Radiator
|
6
|
+
# * graphenej:
|
7
|
+
# * https://github.com/kenCode-de/graphenej/blob/master/graphenej/src/main/java/de/bitsharesmunich/graphenej/Transaction.java#L142
|
8
|
+
class Transaction
|
9
|
+
include ChainConfig
|
10
|
+
include Utils
|
11
|
+
|
12
|
+
VALID_OPTIONS = %w(
|
13
|
+
wif private_key ref_block_num ref_block_prefix expiration operations
|
14
|
+
chain
|
15
|
+
).map(&:to_sym)
|
16
|
+
VALID_OPTIONS.each { |option| attr_accessor option }
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
options.each do |k, v|
|
20
|
+
k = k.to_sym
|
21
|
+
if VALID_OPTIONS.include?(k.to_sym)
|
22
|
+
options.delete(k)
|
23
|
+
send("#{k}=", v)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@logger = options[:logger] || Radiator.logger
|
28
|
+
@chain ||= :steem
|
29
|
+
@chain_id = chain_id options[:chain_id]
|
30
|
+
@url = options[:url] || url
|
31
|
+
@operations ||= []
|
32
|
+
|
33
|
+
unless NETWORK_CHAIN_IDS.include? @chain_id
|
34
|
+
@logger.warn "Unknown chain id: #{@chain_id}"
|
35
|
+
end
|
36
|
+
|
37
|
+
if !!wif && !!private_key
|
38
|
+
raise TransactionError, "Do not pass both wif and private_key. That's confusing."
|
39
|
+
end
|
40
|
+
|
41
|
+
if !!wif
|
42
|
+
@private_key = Bitcoin::Key.from_base58 wif
|
43
|
+
end
|
44
|
+
|
45
|
+
options = options.merge(url: @url)
|
46
|
+
@api = Api.new(options)
|
47
|
+
@network_broadcast_api = NetworkBroadcastApi.new(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def chain_id(chain_id = nil)
|
51
|
+
return chain_id if !!chain_id
|
52
|
+
|
53
|
+
case chain.to_s.downcase.to_sym
|
54
|
+
when :steem then NETWORKS_STEEM_CHAIN_ID
|
55
|
+
when :golos then NETWORKS_GOLOS_CHAIN_ID
|
56
|
+
when :test then NETWORKS_TEST_CHAIN_ID
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def url
|
61
|
+
case chain.to_s.downcase.to_sym
|
62
|
+
when :steem then NETWORKS_STEEM_DEFAULT_NODE
|
63
|
+
when :golos then NETWORKS_GOLOS_DEFAULT_NODE
|
64
|
+
when :test then NETWORKS_TEST_DEFAULT_NODE
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def process(broadcast = false)
|
69
|
+
prepare
|
70
|
+
|
71
|
+
if broadcast
|
72
|
+
@network_broadcast_api.broadcast_transaction_synchronous(payload)
|
73
|
+
else
|
74
|
+
self
|
75
|
+
end
|
76
|
+
end
|
77
|
+
private
|
78
|
+
def payload
|
79
|
+
{
|
80
|
+
expiration: @expiration.strftime('%Y-%m-%dT%H:%M:%S'),
|
81
|
+
ref_block_num: @ref_block_num,
|
82
|
+
ref_block_prefix: @ref_block_prefix,
|
83
|
+
operations: @operations.map { |op| op.payload },
|
84
|
+
extensions: [],
|
85
|
+
signatures: [hexlify(signature)]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def prepare
|
90
|
+
raise TransactionError, "No wif or private key." unless !!@wif || !!@private_key
|
91
|
+
|
92
|
+
@properties = @api.get_dynamic_global_properties.result
|
93
|
+
@ref_block_num = @properties.head_block_number & 0xFFFF
|
94
|
+
@ref_block_prefix = unhexlify(@properties.head_block_id[8..-1]).unpack('V*')[0]
|
95
|
+
|
96
|
+
# The expiration allows for transactions to expire if they are not
|
97
|
+
# included into a block by that time. Always update it to the current
|
98
|
+
# time + EXPIRE_IN_SECS.
|
99
|
+
@expiration = Time.parse(@properties.time + 'Z') + EXPIRE_IN_SECS
|
100
|
+
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_bytes
|
105
|
+
bytes = unhexlify(@chain_id)
|
106
|
+
bytes << pakS(@ref_block_num)
|
107
|
+
bytes << pakI(@ref_block_prefix)
|
108
|
+
bytes << pakI(@expiration.to_i)
|
109
|
+
bytes << pakC(@operations.size)
|
110
|
+
|
111
|
+
@operations.each do |op|
|
112
|
+
bytes << op.to_bytes
|
113
|
+
end
|
114
|
+
|
115
|
+
bytes << 0x00 # extensions
|
116
|
+
|
117
|
+
bytes
|
118
|
+
end
|
119
|
+
|
120
|
+
def digest
|
121
|
+
Digest::SHA256.digest(to_bytes)
|
122
|
+
end
|
123
|
+
|
124
|
+
def signature
|
125
|
+
public_key_hex = @private_key.pub
|
126
|
+
ec = Bitcoin::OpenSSL_EC
|
127
|
+
digest_hex = digest.freeze
|
128
|
+
|
129
|
+
loop do
|
130
|
+
@expiration += 1
|
131
|
+
sig = ec.sign_compact(digest_hex, @private_key.priv, public_key_hex)
|
132
|
+
|
133
|
+
next if public_key_hex != ec.recover_compact(digest_hex, sig)
|
134
|
+
|
135
|
+
return sig if canonical? sig
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def canonical?(sig)
|
140
|
+
sig = sig.unpack('C*')
|
141
|
+
|
142
|
+
!(
|
143
|
+
((sig[0] & 0x80 ) != 0) || ( sig[0] == 0 ) ||
|
144
|
+
((sig[1] & 0x80 ) != 0) ||
|
145
|
+
((sig[32] & 0x80 ) != 0) || ( sig[32] == 0 ) ||
|
146
|
+
((sig[33] & 0x80 ) != 0)
|
147
|
+
)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Radiator
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# See: https://github.com/xeroc/piston-lib/blob/34a7525cee119ec9b24a99577ede2d54466fca0e/steembase/operations.py
|
5
|
+
class Amount
|
6
|
+
def initialize(value)
|
7
|
+
@amount, @asset = value.strip.split(' ')
|
8
|
+
@precision = case @asset
|
9
|
+
when 'STEEM' then 3
|
10
|
+
when 'VESTS' then 6
|
11
|
+
when 'SBD' then 3
|
12
|
+
when 'GOLOS' then 3
|
13
|
+
when 'GESTS' then 6
|
14
|
+
when 'GBG' then 3
|
15
|
+
when 'CORE' then 3
|
16
|
+
when 'CESTS' then 6
|
17
|
+
when 'TEST' then 3
|
18
|
+
else; raise TypeError, "Asset #{@asset} unknown."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_bytes
|
23
|
+
asset = @asset.ljust(7, "\x00")
|
24
|
+
amount = (@amount.to_f * 10 ** @precision).round
|
25
|
+
|
26
|
+
[amount].pack('q') +
|
27
|
+
[@precision].pack('c') +
|
28
|
+
asset
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"#{@amount} #{@asset}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Radiator
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# See: https://github.com/xeroc/piston-lib/blob/34a7525cee119ec9b24a99577ede2d54466fca0e/steembase/operations.py
|
5
|
+
class Permission
|
6
|
+
def initialize()
|
7
|
+
raise NotImplementedError, 'stub'
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_bytes
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Radiator
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# See: https://github.com/xeroc/python-graphenelib/blob/98de98e219264d45fe04b3c28f3aabd1a9f58b71/graphenebase/types.py
|
5
|
+
class PointInTime
|
6
|
+
def initialize(value)
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_bytes
|
11
|
+
[Time.parse(@value + 'Z').to_i].pack('I')
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Radiator
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# See: https://github.com/xeroc/piston-lib/blob/34a7525cee119ec9b24a99577ede2d54466fca0e/steembase/operations.py
|
5
|
+
class PublicKey
|
6
|
+
def initialize()
|
7
|
+
raise NotImplementedError, 'stub'
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_bytes
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Radiator
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# See: https://github.com/xeroc/piston-lib/blob/34a7525cee119ec9b24a99577ede2d54466fca0e/steembase/operations.py
|
5
|
+
class Uint16
|
6
|
+
def initialize(value)
|
7
|
+
@value = value.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_bytes
|
11
|
+
[@value].pack('S')
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@value.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Radiator
|
2
|
+
module Type
|
3
|
+
|
4
|
+
# See: https://github.com/xeroc/piston-lib/blob/34a7525cee119ec9b24a99577ede2d54466fca0e/steembase/operations.py
|
5
|
+
class Uint32
|
6
|
+
def initialize(value)
|
7
|
+
@value = value.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_bytes
|
11
|
+
[@value].pack('L')
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@value.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|