beowulf-ruby-testnet 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +58 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +204 -0
- data/Rakefile +52 -0
- data/beowulf.gemspec +43 -0
- data/lib/beowulf.rb +35 -0
- data/lib/beowulf/account_history_api.rb +15 -0
- data/lib/beowulf/api.rb +845 -0
- data/lib/beowulf/base_error.rb +21 -0
- data/lib/beowulf/block_api.rb +14 -0
- data/lib/beowulf/broadcast_operations.json +74 -0
- data/lib/beowulf/chain_config.rb +15 -0
- data/lib/beowulf/condenser_api.rb +48 -0
- data/lib/beowulf/database_api.rb +5 -0
- data/lib/beowulf/error_parser.rb +228 -0
- data/lib/beowulf/logger.rb +20 -0
- data/lib/beowulf/methods.json +129 -0
- data/lib/beowulf/network_broadcast_api.rb +7 -0
- data/lib/beowulf/operation.rb +99 -0
- data/lib/beowulf/operation_ids.rb +33 -0
- data/lib/beowulf/operation_types.rb +31 -0
- data/lib/beowulf/transaction.rb +320 -0
- data/lib/beowulf/type/amount.rb +23 -0
- data/lib/beowulf/type/array.rb +17 -0
- data/lib/beowulf/type/authority.rb +48 -0
- data/lib/beowulf/type/authority_update.rb +50 -0
- data/lib/beowulf/type/future.rb +16 -0
- data/lib/beowulf/type/hash.rb +17 -0
- data/lib/beowulf/type/permission.rb +17 -0
- data/lib/beowulf/type/point_in_time.rb +17 -0
- data/lib/beowulf/type/public_key.rb +18 -0
- data/lib/beowulf/type/serializer.rb +12 -0
- data/lib/beowulf/type/u_int16.rb +17 -0
- data/lib/beowulf/type/u_int32.rb +17 -0
- data/lib/beowulf/utils.rb +221 -0
- data/lib/beowulf/version.rb +4 -0
- data/lib/beowulf/wallet.rb +212 -0
- metadata +463 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Beowulf
|
2
|
+
class BaseError < StandardError
|
3
|
+
def initialize(error, cause = nil)
|
4
|
+
@error = error
|
5
|
+
@cause = cause
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
if !!@cause
|
10
|
+
JSON[error: @error, cause: @cause] rescue {error: @error, cause: @cause}.to_s
|
11
|
+
else
|
12
|
+
JSON[@error] rescue @error
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Beowulf; class ApiError < BaseError; end; end
|
19
|
+
module Beowulf; class TypeError < BaseError; end; end
|
20
|
+
module Beowulf; class OperationError < BaseError; end; end
|
21
|
+
module Beowulf; class TransactionError < BaseError; end; end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"roles": ["owner"],
|
4
|
+
"operation": "transfer",
|
5
|
+
"params": [
|
6
|
+
"from",
|
7
|
+
"to",
|
8
|
+
"amount",
|
9
|
+
"fee",
|
10
|
+
"memo"
|
11
|
+
]
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"roles": ["owner"],
|
15
|
+
"operation": "transfer_to_vesting",
|
16
|
+
"params": [
|
17
|
+
"from",
|
18
|
+
"to",
|
19
|
+
"amount",
|
20
|
+
"fee"
|
21
|
+
]
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"roles": ["owner"],
|
25
|
+
"operation": "withdraw_vesting",
|
26
|
+
"params": [
|
27
|
+
"account",
|
28
|
+
"vesting_shares",
|
29
|
+
"fee"
|
30
|
+
]
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"roles": ["owner"],
|
34
|
+
"operation": "account_create",
|
35
|
+
"params": [
|
36
|
+
"fee",
|
37
|
+
"creator",
|
38
|
+
"new_account_name",
|
39
|
+
"owner",
|
40
|
+
"json_metadata"
|
41
|
+
]
|
42
|
+
},
|
43
|
+
{
|
44
|
+
"roles": ["owner"],
|
45
|
+
"operation": "account_update",
|
46
|
+
"params": [
|
47
|
+
"account",
|
48
|
+
"owner",
|
49
|
+
"json_metadata",
|
50
|
+
"fee"
|
51
|
+
]
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"roles": ["owner"],
|
55
|
+
"operation": "supernode_update",
|
56
|
+
"params": [
|
57
|
+
"owner",
|
58
|
+
"url",
|
59
|
+
"block_signing_key",
|
60
|
+
"props",
|
61
|
+
"fee"
|
62
|
+
]
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"roles": ["owner"],
|
66
|
+
"operation": "account_supernode_vote",
|
67
|
+
"params": [
|
68
|
+
"account",
|
69
|
+
"supernode",
|
70
|
+
"approve",
|
71
|
+
"fee"
|
72
|
+
]
|
73
|
+
}
|
74
|
+
]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Beowulf
|
2
|
+
module ChainConfig
|
3
|
+
EXPIRE_IN_SECS = 600
|
4
|
+
EXPIRE_IN_SECS_PROPOSAL = 24 * 60 * 60
|
5
|
+
|
6
|
+
NETWORKS_BEOWULF_CHAIN_ID = '430b37f23cf146d42f15376f341d7f8f5a1ad6f4e63affdeb5dc61d55d8c95a7'
|
7
|
+
NETWORKS_BEOWULF_ADDRESS_PREFIX = 'BEO'
|
8
|
+
NETWORKS_BEOWULF_BWF_ASSET = 'BWF'
|
9
|
+
NETWORKS_BEOWULF_W_ASSET = 'W'
|
10
|
+
NETWORKS_BEOWULF_VEST_ASSET = 'M'
|
11
|
+
NETWORKS_BEOWULF_DEFAULT_NODE = 'https://testnet-bw.beowulfchain.com/rpc'
|
12
|
+
|
13
|
+
NETWORK_CHAIN_IDS = [NETWORKS_BEOWULF_CHAIN_ID]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Beowulf
|
2
|
+
class CondenserApi < Api
|
3
|
+
METHOD_NAMES = [
|
4
|
+
# :broadcast_block,
|
5
|
+
:broadcast_transaction,
|
6
|
+
:broadcast_transaction_synchronous,
|
7
|
+
:get_account_count,
|
8
|
+
:get_accounts,
|
9
|
+
:get_balance,
|
10
|
+
:get_active_supernodes,
|
11
|
+
:get_block,
|
12
|
+
:get_block_header,
|
13
|
+
:get_config,
|
14
|
+
:get_dynamic_global_properties,
|
15
|
+
:get_hardfork_version,
|
16
|
+
:get_key_references,
|
17
|
+
:get_next_scheduled_hardfork,
|
18
|
+
:get_owner_history,
|
19
|
+
:get_potential_signatures,
|
20
|
+
:get_required_signatures,
|
21
|
+
:get_transaction,
|
22
|
+
:get_transaction_hex,
|
23
|
+
:get_transaction_with_status,
|
24
|
+
:get_version,
|
25
|
+
:get_supernode_by_account,
|
26
|
+
:get_supernode_count,
|
27
|
+
:get_supernode_schedule,
|
28
|
+
:get_supernodes,
|
29
|
+
:get_supernodes_by_vote,
|
30
|
+
:lookup_account_names,
|
31
|
+
:lookup_accounts,
|
32
|
+
:lookup_supernode_accounts,
|
33
|
+
:list_smt_tokens,
|
34
|
+
:verify_authority,
|
35
|
+
:find_smt_tokens_by_name,
|
36
|
+
:get_supernode_voted_by_acc,
|
37
|
+
:get_pending_transaction_count
|
38
|
+
].freeze
|
39
|
+
|
40
|
+
def method_names
|
41
|
+
METHOD_NAMES
|
42
|
+
end
|
43
|
+
|
44
|
+
def api_name
|
45
|
+
:condenser_api
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
module Beowulf
|
2
|
+
class ErrorParser
|
3
|
+
include Utils
|
4
|
+
|
5
|
+
attr_reader :response, :error, :error_code, :error_message,
|
6
|
+
:api_name, :api_method, :api_params,
|
7
|
+
:expiry, :can_retry, :can_reprepare, :node_degraded, :trx_id, :debug
|
8
|
+
|
9
|
+
alias expiry? expiry
|
10
|
+
alias can_retry? can_retry
|
11
|
+
alias can_reprepare? can_reprepare
|
12
|
+
alias node_degraded? node_degraded
|
13
|
+
|
14
|
+
REPREPARE_WHITELIST = [
|
15
|
+
'is_canonical( c ): signature is not canonical',
|
16
|
+
'now < trx.expiration: '
|
17
|
+
]
|
18
|
+
|
19
|
+
DUPECHECK = '(skip & skip_transaction_dupe_check) || trx_idx.indices().get<by_trx_id>().find(trx_id) == trx_idx.indices().get<by_trx_id>().end(): Duplicate transaction check failed'
|
20
|
+
|
21
|
+
REPREPARE_BLACKLIST = [DUPECHECK]
|
22
|
+
|
23
|
+
def initialize(response)
|
24
|
+
@response = response
|
25
|
+
|
26
|
+
@error = nil
|
27
|
+
@error_code = nil
|
28
|
+
@error_message = nil
|
29
|
+
@api_name = nil
|
30
|
+
@api_method = nil
|
31
|
+
@api_params = nil
|
32
|
+
|
33
|
+
@expiry = nil
|
34
|
+
@can_retry = nil
|
35
|
+
@can_reprepare = nil
|
36
|
+
@trx_id = nil
|
37
|
+
@debug = nil
|
38
|
+
|
39
|
+
parse_error_response
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_error_response
|
43
|
+
if response.nil?
|
44
|
+
@expiry = false
|
45
|
+
@can_retry = false
|
46
|
+
@can_reprepare = false
|
47
|
+
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
@response = JSON[response] if response.class == String
|
52
|
+
|
53
|
+
@error = if !!@response['error']
|
54
|
+
response['error']
|
55
|
+
else
|
56
|
+
response
|
57
|
+
end
|
58
|
+
|
59
|
+
begin
|
60
|
+
if !!@error['data']
|
61
|
+
# These are, by far, the more interesting errors, so we try to pull
|
62
|
+
# them out first, if possible.
|
63
|
+
|
64
|
+
@error_code = @error['data']['code']
|
65
|
+
stacks = @error['data']['stack']
|
66
|
+
stack_formats = nil
|
67
|
+
|
68
|
+
@error_message = if !!stacks
|
69
|
+
stack_formats = stacks.map { |s| s['format'] }
|
70
|
+
stack_datum = stacks.map { |s| s['data'] }
|
71
|
+
data_call_method = stack_datum.find { |data| data['call.method'] == 'call' }
|
72
|
+
data_name = stack_datum.find { |data| !!data['name'] }
|
73
|
+
|
74
|
+
# See if we can recover a transaction id out of this hot mess.
|
75
|
+
data_trx_ix = stack_datum.find { |data| !!data['trx_ix'] }
|
76
|
+
@trx_id = data_trx_ix['trx_ix'] if !!data_trx_ix
|
77
|
+
|
78
|
+
stack_formats.reject(&:empty?).join('; ')
|
79
|
+
else
|
80
|
+
@error_code ||= @error['code']
|
81
|
+
@error['message']
|
82
|
+
end
|
83
|
+
|
84
|
+
@api_name, @api_method, @api_params = if !!data_call_method
|
85
|
+
@api_name = data_call_method['call.params']
|
86
|
+
end
|
87
|
+
else
|
88
|
+
@error_code = @error['code']
|
89
|
+
@error_message = @error['message']
|
90
|
+
@expiry = false
|
91
|
+
@can_retry = false
|
92
|
+
@can_reprepare = false
|
93
|
+
end
|
94
|
+
|
95
|
+
case @error_code
|
96
|
+
when -32603
|
97
|
+
if error_match?('Internal Error')
|
98
|
+
@expiry = false
|
99
|
+
@can_retry = true
|
100
|
+
@can_reprepare = true
|
101
|
+
end
|
102
|
+
when -32003
|
103
|
+
if error_match?('Unable to acquire database lock')
|
104
|
+
@expiry = false
|
105
|
+
@can_retry = true
|
106
|
+
@can_reprepare = true
|
107
|
+
end
|
108
|
+
when -32000
|
109
|
+
@expiry = false
|
110
|
+
@can_retry = coerce_backtrace
|
111
|
+
@can_reprepare = if @api_name == 'network_broadcast_api'
|
112
|
+
error_match(REPREPARE_WHITELIST)
|
113
|
+
else
|
114
|
+
false
|
115
|
+
end
|
116
|
+
when 10
|
117
|
+
@expiry = false
|
118
|
+
@can_retry = coerce_backtrace
|
119
|
+
@can_reprepare = !!stack_formats && (stack_formats & REPREPARE_WHITELIST).any?
|
120
|
+
when 13
|
121
|
+
@error_message = @error['data']['message']
|
122
|
+
@expiry = false
|
123
|
+
@can_retry = false
|
124
|
+
@can_reprepare = false
|
125
|
+
when 3030000
|
126
|
+
@error_message = @error['data']['message']
|
127
|
+
@expiry = false
|
128
|
+
@can_retry = false
|
129
|
+
@can_reprepare = false
|
130
|
+
when 4030100
|
131
|
+
# Code 4030100 is "transaction_expiration_exception: transaction
|
132
|
+
# expiration exception". If we assume the expiration was valid, the
|
133
|
+
# node might be bad and needs to be dropped.
|
134
|
+
|
135
|
+
@expiry = true
|
136
|
+
@can_retry = true
|
137
|
+
@can_reprepare = false
|
138
|
+
when 4030200
|
139
|
+
# Code 4030200 is "transaction tapos exception". They are recoverable
|
140
|
+
# if the transaction hasn't expired yet. A tapos exception can be
|
141
|
+
# retried in situations where the node is behind and the tapos is
|
142
|
+
# based on a block the node doesn't know about yet.
|
143
|
+
|
144
|
+
@expiry = false
|
145
|
+
@can_retry = true
|
146
|
+
|
147
|
+
# Allow fall back to reprepare if retry fails.
|
148
|
+
@can_reprepare = true
|
149
|
+
else
|
150
|
+
@expiry = false
|
151
|
+
@can_retry = false
|
152
|
+
@can_reprepare = false
|
153
|
+
end
|
154
|
+
rescue => e
|
155
|
+
if defined? ap
|
156
|
+
if ENV['DEBUG'] == 'true'
|
157
|
+
ap error_parser_exception: e, original_response: response, backtrace: e.backtrace
|
158
|
+
else
|
159
|
+
ap error_parser_exception: e, original_response: response
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
@expiry = false
|
164
|
+
@can_retry = false
|
165
|
+
@can_reprepare = false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def coerce_backtrace
|
170
|
+
can_retry = false
|
171
|
+
|
172
|
+
case @error['code']
|
173
|
+
when -32003
|
174
|
+
any_of = [
|
175
|
+
'Internal Error"',
|
176
|
+
'_api_plugin not enabled.'
|
177
|
+
]
|
178
|
+
|
179
|
+
can_retry = error_match?('Unable to acquire database lock')
|
180
|
+
|
181
|
+
if !can_retry && error_match?(any_of)
|
182
|
+
can_retry = true
|
183
|
+
@node_degraded = true
|
184
|
+
else
|
185
|
+
@node_degraded = false
|
186
|
+
end
|
187
|
+
when -32002
|
188
|
+
can_retry = @node_degraded = error_match?('Could not find API')
|
189
|
+
when 1
|
190
|
+
can_retry = @node_degraded = error_match?('no method with name \'condenser_api')
|
191
|
+
end
|
192
|
+
|
193
|
+
can_retry
|
194
|
+
end
|
195
|
+
|
196
|
+
def error_match?(matches)
|
197
|
+
matches = [matches].flatten
|
198
|
+
|
199
|
+
any = matches.map do |match|
|
200
|
+
case match
|
201
|
+
when String
|
202
|
+
@error['message'] && @error['message'].include?(match)
|
203
|
+
when ::Array
|
204
|
+
if @error['message']
|
205
|
+
match.map { |m| m.include?(match) }.include? true
|
206
|
+
else
|
207
|
+
false
|
208
|
+
end
|
209
|
+
else; false
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
any.include?(true)
|
214
|
+
end
|
215
|
+
|
216
|
+
def to_s
|
217
|
+
if !!error_message && !error_message.empty?
|
218
|
+
"#{error_code}: #{error_message}"
|
219
|
+
else
|
220
|
+
error_code.to_s
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def inspect
|
225
|
+
"#<#{self.class.name} [#{to_s}]>"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Beowulf
|
4
|
+
# The logger that Beowulf uses for reporting errors.
|
5
|
+
#
|
6
|
+
# @return [Logger]
|
7
|
+
def self.logger
|
8
|
+
@@logger ||= Logger.new(STDOUT).tap do |logger|
|
9
|
+
logger.progname = 'beowulf'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Sets the logger that Beowulf uses for reporting errors.
|
14
|
+
#
|
15
|
+
# @param logger [Logger] The logger to set as Beowulf's logger.
|
16
|
+
# @return [void]
|
17
|
+
def self.logger=(logger)
|
18
|
+
@@logger = logger
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"api": "database_api",
|
4
|
+
"method": "get_block_header",
|
5
|
+
"params": ["blockNum"]
|
6
|
+
},
|
7
|
+
{
|
8
|
+
"api": "database_api",
|
9
|
+
"method": "get_block",
|
10
|
+
"params": ["blockNum"]
|
11
|
+
},
|
12
|
+
{
|
13
|
+
"api": "database_api",
|
14
|
+
"method": "get_config"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"api": "database_api",
|
18
|
+
"method": "get_dynamic_global_properties"
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"api": "database_api",
|
22
|
+
"method": "get_supernode_schedule"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
"api": "database_api",
|
26
|
+
"method": "get_hardfork_version"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"api": "database_api",
|
30
|
+
"method": "get_next_scheduled_hardfork"
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"api": "account_by_key_api",
|
34
|
+
"method": "get_key_references",
|
35
|
+
"params": ["key"]
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"api": "database_api",
|
39
|
+
"method": "get_accounts",
|
40
|
+
"params": ["names"]
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"api": "database_api",
|
44
|
+
"method": "lookup_account_names",
|
45
|
+
"params": ["accountNames"]
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"api": "database_api",
|
49
|
+
"method": "lookup_accounts",
|
50
|
+
"params": ["lowerBoundName", "limit"]
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"api": "database_api",
|
54
|
+
"method": "get_account_count"
|
55
|
+
},
|
56
|
+
{
|
57
|
+
"api": "database_api",
|
58
|
+
"method": "get_owner_history",
|
59
|
+
"params": ["account"]
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"api": "database_api",
|
63
|
+
"method": "get_transaction_hex",
|
64
|
+
"params": ["trx"]
|
65
|
+
},
|
66
|
+
{
|
67
|
+
"api": "database_api",
|
68
|
+
"method": "get_transaction",
|
69
|
+
"params": ["trxId"]
|
70
|
+
},
|
71
|
+
{
|
72
|
+
"api": "database_api",
|
73
|
+
"method": "get_required_signatures",
|
74
|
+
"params": ["trx", "availableKeys"]
|
75
|
+
},
|
76
|
+
{
|
77
|
+
"api": "database_api",
|
78
|
+
"method": "get_potential_signatures",
|
79
|
+
"params": ["trx"]
|
80
|
+
},
|
81
|
+
{
|
82
|
+
"api": "database_api",
|
83
|
+
"method": "verify_authority",
|
84
|
+
"params": ["trx"]
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"api": "database_api",
|
88
|
+
"method": "get_supernodes",
|
89
|
+
"params": ["supernodeIds"]
|
90
|
+
},
|
91
|
+
{
|
92
|
+
"api": "database_api",
|
93
|
+
"method": "get_supernode_by_account",
|
94
|
+
"params": ["accountName"]
|
95
|
+
},
|
96
|
+
{
|
97
|
+
"api": "database_api",
|
98
|
+
"method": "get_supernodes_by_vote",
|
99
|
+
"params": ["from", "limit"]
|
100
|
+
},
|
101
|
+
{
|
102
|
+
"api": "database_api",
|
103
|
+
"method": "lookup_supernode_accounts",
|
104
|
+
"params": ["lowerBoundName", "limit"]
|
105
|
+
},
|
106
|
+
{
|
107
|
+
"api": "database_api",
|
108
|
+
"method": "get_supernode_count"
|
109
|
+
},
|
110
|
+
{
|
111
|
+
"api": "database_api",
|
112
|
+
"method": "get_active_supernodes"
|
113
|
+
},
|
114
|
+
{
|
115
|
+
"api": "network_broadcast_api",
|
116
|
+
"method": "broadcast_transaction",
|
117
|
+
"params": ["trx"]
|
118
|
+
},
|
119
|
+
{
|
120
|
+
"api": "network_broadcast_api",
|
121
|
+
"method": "broadcast_transaction_synchronous",
|
122
|
+
"params": ["trx"]
|
123
|
+
},
|
124
|
+
{
|
125
|
+
"api": "network_broadcast_api",
|
126
|
+
"method": "broadcast_block",
|
127
|
+
"params": ["b"]
|
128
|
+
}
|
129
|
+
]
|